{"id":15293532,"url":"https://github.com/material-motion/indefinite-observable-swift","last_synced_at":"2025-10-07T06:30:45.850Z","repository":{"id":56915455,"uuid":"75447903","full_name":"material-motion/indefinite-observable-swift","owner":"material-motion","description":"A generic Observable for Swift","archived":true,"fork":false,"pushed_at":"2018-04-15T05:58:23.000Z","size":60,"stargazers_count":8,"open_issues_count":1,"forks_count":2,"subscribers_count":10,"default_branch":"develop","last_synced_at":"2024-04-12T16:07:46.779Z","etag":null,"topics":["observables","swift"],"latest_commit_sha":null,"homepage":"http://cocoadocs.org/docsets/IndefiniteObservable/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/material-motion.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-12-03T02:57:21.000Z","updated_at":"2023-01-28T05:58:12.000Z","dependencies_parsed_at":"2022-08-21T02:50:58.595Z","dependency_job_id":null,"html_url":"https://github.com/material-motion/indefinite-observable-swift","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/material-motion%2Findefinite-observable-swift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/material-motion%2Findefinite-observable-swift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/material-motion%2Findefinite-observable-swift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/material-motion%2Findefinite-observable-swift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/material-motion","download_url":"https://codeload.github.com/material-motion/indefinite-observable-swift/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235599682,"owners_count":19016190,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["observables","swift"],"created_at":"2024-09-30T16:49:53.357Z","updated_at":"2025-10-07T06:30:40.501Z","avatar_url":"https://github.com/material-motion.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IndefiniteObservable.swift\n\n[![Build Status](https://travis-ci.org/material-motion/indefinite-observable-swift.svg?branch=develop)](https://travis-ci.org/material-motion/indefinite-observable-swift)\n[![codecov](https://codecov.io/gh/material-motion/indefinite-observable-swift/branch/develop/graph/badge.svg)](https://codecov.io/gh/material-motion/indefinite-observable-swift)\n[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/IndefiniteObservable.svg)](https://cocoapods.org/pods/IndefiniteObservable)\n[![Platform](https://img.shields.io/cocoapods/p/IndefiniteObservable.svg)](http://cocoadocs.org/docsets/IndefiniteObservable)\n[![Docs](https://img.shields.io/cocoapods/metrics/doc-percent/IndefiniteObservable.svg)](http://cocoadocs.org/docsets/IndefiniteObservable)\n\n`IndefiniteObservable` is a minimal implementation of [Observable](http://reactivex.io/rxjs/manual/overview.html)\nwith no concept of completion or failure.\n\n## Supported languages\n\n- Swift 3\n\nThis library does not support Objective-C due to its heavy use of generics.\n\n## Installation\n\n### Installation with CocoaPods\n\n\u003e CocoaPods is a dependency manager for Objective-C and Swift libraries. CocoaPods automates the\n\u003e process of using third-party libraries in your projects. See\n\u003e [the Getting Started guide](https://guides.cocoapods.org/using/getting-started.html) for more\n\u003e information. You can install it with the following command:\n\u003e\n\u003e     gem install cocoapods\n\nAdd `IndefiniteObservable` to your `Podfile`:\n\n    pod 'IndefiniteObservable'\n\nThen run the following command:\n\n    pod install\n    \n### Installation with Swift Package Manager\n\nCreate a `Package.swift` file.\n\n```swift\nimport PackageDescription\n\nlet package = Package(\n    name: \"YourProject\",\n    targets: [],\n    dependencies: [\n        .Package(url: \"https://github.com/material-motion/indefinite-observable-swift.git”, majorVersion: 4)\n    ]\n)\n```\n\n`swift build`\n    \n### Usage\n\nImport the framework:\n\n    @import IndefiniteObservable;\n\nYou will now have access to all of the APIs.\n\n## Example apps/unit tests\n\nCheck out a local copy of the repo to access the Catalog application by running the following\ncommands:\n\n    git clone https://github.com/material-motion/indefinite-observable-swift.git\n    cd observable-swift\n    pod install\n    open IndefiniteObservable.xcworkspace\n\n# Guides\n\n1. [How to make an observable](#how-to-make-an-observable)\n1. [How to create a synchronous stream](#how-to-create-a-synchronous-stream)\n1. [How to create an asynchronous stream using blocks](#how-to-create-an-asynchronous-stream-using-blocks)\n1. [How to subscribe to a stream](#how-to-subscribe-to-a-stream)\n1. [How to unsubscribe from a stream](#how-to-unsubscribe-from-a-stream)\n1. [How to create an synchronous stream using objects](#how-to-create-an-synchronous-stream-using-objects)\n\n## How to make an observable\n\nIn this example we'll make the simplest possible observable type: a value observable. We will use\nthis concrete type in all of the following guides.\n\n```swift\nfinal class ValueObserver\u003cT\u003e: Observer {\n  typealias Value = T\n\n  init(_ next: @escaping (T) -\u003e Void) {\n    self.next = next\n  }\n\n  let next: (T) -\u003e Void\n}\n\nfinal class ValueObservable\u003cT\u003e: IndefiniteObservable\u003cValueObserver\u003cT\u003e\u003e {\n  func subscribe(_ next: @escaping (T) -\u003e Void) -\u003e Subscription {\n    return super.subscribe(observer: ValueObserver(next))\n  }\n}\n```\n\n## How to create a synchronous stream\n\n```swift\nlet observable = ValueObservable\u003c\u003c#ValueType#\u003e\u003e { observer in\n  observer.next(\u003c#value#\u003e)\n  return noopDisconnect\n}\n```\n\n## How to create an asynchronous stream using blocks\n\nIf you have an API that provides a block-based mechanism for registering observers then you can\ncreate an asynchronous stream in place like so:\n\n```swift\nlet observable = ValueObservable\u003c\u003c#ValueType#\u003e\u003e { observer in\n  let someToken = registerSomeCallback { callbackValue in\n    observer.next(callbackValue)\n  }\n\n  return {\n    unregisterCallback(someToken)\n  }\n}\n```\n\n## How to subscribe to a stream\n\n```swift\nobservable.subscribe { value in\n  print(value)\n}\n```\n\n## How to unsubscribe from a stream\n\nUnsubscribing will invoke the observable's disconnect method. To unsubscribe, you must retain a\nreference to the subscription instance returned by subscribe.\n\n```swift\nlet subscription = observable.subscribe { value in\n  print(value)\n}\n\nsubscription.unsubscribe()\n```\n\n## How to create an synchronous stream using objects\n\nMany iOS/macOS APIs use delegation for event handling. To connect delegates with a stream you will\nneed to create a `Producer` class. A `Producer` listens for events with an event delegate like\n`didTap` and forwards those events to an IndefiniteObservable's observer.\n\n### Final result\n\n```swift\nclass DragConnection {\n  typealias Value = (state: UIGestureRecognizerState, location: CGPoint)\n\n  init(subscribedTo gesture: UIPanGestureRecognizer, observer: ValueObserver\u003cValue\u003e) {\n    self.gesture = gesture\n    self.observer = observer\n\n    gesture.addTarget(self, action: #selector(didPan))\n\n    // Populate the observer with the current gesture state.\n    observer.next(currentValue(for: gesture))\n  }\n\n  @objc func didPan(_ gesture: UIPanGestureRecognizer) {\n    observer.next(currentValue(for: gesture))\n  }\n\n  func currentValue(for gesture: UIPanGestureRecognizer) -\u003e Value {\n    return (gesture.state, gesture.location(in: gesture.view!))\n  }\n\n  func disconnect() {\n    gesture?.removeTarget(self, action: #selector(didPan))\n    gesture = nil\n  }\n\n  var gesture: (UIPanGestureRecognizer)?\n  let observer: ValueObserver\u003cValue\u003e\n}\n\nlet pan = UIPanGestureRecognizer()\nview.addGestureRecognizer(pan)\n\nlet dragStream = ValueObservable\u003cDragConnection.Value\u003e { observer in\n  return DragConnection(subscribedTo: pan, observer: observer).disconnect\n}\nlet subscription = dragStream.subscribe {\n  dump($0.state)\n  dump($0.location)\n}\n```\n\n### Step 1: Define the Producer type\n\nA Producer should be a type of Subscription.\n\n```swift\nclass \u003c#Name#\u003eProducer: Subscription {\n}\n```\n\n### Step 2: Define the Value type\n\n```swift\nclass DragConnection: Subscription {\n  typealias Value = (state: UIGestureRecognizerState, location: CGPoint)\n}\n```\n\n### Step 3: Implement the initializer\n\nYour initializer must accept and store an `ValueObserver\u003cValue\u003e` instance.\n\n```swift\n  init(subscribedTo gesture: UIPanGestureRecognizer, observer: ValueObserver\u003cValue\u003e) {\n    self.gesture = gesture\n    self.observer = observer\n  }\n\n  var gesture: (UIPanGestureRecognizer)?\n  let observer: ValueObserver\u003cValue\u003e\n```\n\n### Step 4: Connect to the event source and send values to the observer\n\n```swift\n  init(subscribedTo gesture: UIPanGestureRecognizer, observer: ValueObserver\u003cValue\u003e) {\n    ...\n\n    gesture.addTarget(self, action: #selector(didPan))\n  }\n\n  @objc func didPan(_ gesture: UIPanGestureRecognizer) {\n    observer.next(currentValue(for: gesture))\n  }\n\n  func currentValue(for gesture: UIPanGestureRecognizer) -\u003e Value {\n    return (gesture.state, gesture.location(in: gesture.view!))\n  }\n```\n\n### Step 5: Implement disconnect\n\nYou are responsible for disconnecting from and releasing any resources here.\n\n```swift\n  func disconnect() {\n    gesture?.removeTarget(self, action: #selector(didPan))\n    gesture = nil\n  }\n```\n\n### Step 6: (Optional) Provide the initial state\n\nIt often is helpful to provide the observer with the current state on registration.\n\n```swift\n  init(subscribedTo gesture: UIPanGestureRecognizer, observer: ValueObserver\u003cValue\u003e) {\n    ...\n\n    // Populate the observer with the current gesture state.\n    observer.next(currentValue(for: gesture))\n  }\n```\n\n### Step 7: Observe the producer\n\n```swift\nlet dragStream = ValueObservable\u003cDragConnection.Value\u003e { observer in\n  return DragConnection(subscribedTo: pan, observer: observer).disconnect\n}\nlet subscription = dragStream.subscribe {\n  dump($0)\n}\n```\n\n## Contributing\n\nThis library is meant to be a minimal implementation that never grows. As such, we only encourage\ncontributions in the form of documentation, tests, and examples.\n\nLearn more about [our team](https://material-motion.github.io/material-motion/team/),\n[our community](https://material-motion.github.io/material-motion/team/community/), and\nour [contributor essentials](https://material-motion.github.io/material-motion/team/essentials/).\n\n## License\n\nLicensed under the Apache 2.0 license. See LICENSE for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaterial-motion%2Findefinite-observable-swift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaterial-motion%2Findefinite-observable-swift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaterial-motion%2Findefinite-observable-swift/lists"}