Skip to content
This repository has been archived by the owner on Oct 14, 2021. It is now read-only.

Commit

Permalink
Merge branch 'release-candidate' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Verkoeyen committed Dec 13, 2016
2 parents e95f516 + 755e10c commit 43420ec
Show file tree
Hide file tree
Showing 12 changed files with 101 additions and 89 deletions.
10 changes: 0 additions & 10 deletions .jazzy.yaml

This file was deleted.

1 change: 0 additions & 1 deletion .swift-version

This file was deleted.

2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
language: objective-c
osx_image: xcode8.1
sudo: false
notifications:
email: false
before_install:
- gem install cocoapods --no-rdoc --no-ri --no-document --quiet
- git clone https://github.com/phacility/arcanist.git
Expand Down
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
# 3.0.0

There is now a distinction between **connection** and **subscription**.

## Breaking changes

- `Subscription` is now a public concrete type.
- `noopUnsubscription` has been renamed to `noopDisconnect`.
- IndefiniteObservable now accepts a Connect function that returns a non-optional Disconnect
function.

## Source changes

* [Rename noopDisconnection to noopDisconnect.](https://github.com/material-motion/indefinite-observable-swift/commit/2ba6492b6020fe0e921cff1abe555c2550a9aaac) (Jeff Verkoeyen)
* [Introduce distinction between connection and subscription.](https://github.com/material-motion/indefinite-observable-swift/commit/441707babaf57a16975a8db32bd7dbe8fbabff7a) (Jeff Verkoeyen)

## API changes

Auto-generated by running:

apidiff origin/stable release-candidate swift IndefiniteObservable.xcworkspace IndefiniteObservable

## Subscription

*new* class: `Subscription`

*removed* protocol: `Subscription`

## noopDisconnect

*new* global var: `noopDisconnect`

## noopUnsubscription

*removed* global var: `noopUnsubscription`

## Non-source changes

* [Automatic changelog preparation for release.](https://github.com/material-motion/indefinite-observable-swift/commit/f8bf06348285b0da9bad14aec274db2177f67cb5) (Jeff Verkoeyen)
* [Remove .jazzy.yaml and .swift-version](https://github.com/material-motion/indefinite-observable-swift/commit/cb3a4994d37c62fe1896821f60c6597408078439) (Jeff Verkoeyen)
* [Send emails on build failures.](https://github.com/material-motion/indefinite-observable-swift/commit/18f7e09ba19f4536f2c4bf710fae9e77c0acac36) (Jeff Verkoeyen)

# 2.0.0

IndefiniteObservable now supports novel Observer implementations using a genericized Observer type.
Expand Down
2 changes: 1 addition & 1 deletion IndefiniteObservable.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "IndefiniteObservable"
s.summary = "IndefiniteObservable is a minimal implementation of Observable with no concept of completion or failure."
s.version = "2.0.0"
s.version = "3.0.0"
s.authors = "The Material Motion Authors"
s.license = "Apache 2.0"
s.homepage = "https://github.com/material-motion/indefinite-observable-swift"
Expand Down
6 changes: 3 additions & 3 deletions Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
PODS:
- CatalogByConvention (2.0.0)
- IndefiniteObservable/examples (2.0.0):
- IndefiniteObservable/examples (3.0.0):
- IndefiniteObservable/lib
- IndefiniteObservable/lib (2.0.0)
- IndefiniteObservable/lib (3.0.0)

DEPENDENCIES:
- CatalogByConvention
Expand All @@ -15,7 +15,7 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
CatalogByConvention: be55c2263132e4f9f59299ac8a528ee8715b3275
IndefiniteObservable: 66776cf0f5929a5a260bf04d340bfb0a7087d56f
IndefiniteObservable: 65f2dbdba39fb9c175c99c1cc927bf1dabc7bcc9

PODFILE CHECKSUM: 3e4cdba95901e07a289159f0c5a8b830ecb1a5c8

Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ final class ValueObservable<T>: IndefiniteObservable<ValueObserver<T>> {
```swift
let observable = ValueObservable<<#ValueType#>> { observer in
observer.next(<#value#>)
return noopUnsubscription
return noopDisconnect
}
```

Expand Down Expand Up @@ -138,7 +138,7 @@ need to create a `Producer` class. A `Producer` listens for events with an event
### Final result

```swift
class DragProducer: Subscription {
class DragConnection {
typealias Value = (state: UIGestureRecognizerState, location: CGPoint)

init(subscribedTo gesture: UIPanGestureRecognizer, observer: ValueObserver<Value>) {
Expand All @@ -159,7 +159,7 @@ class DragProducer: Subscription {
return (gesture.state, gesture.location(in: gesture.view!))
}

func unsubscribe() {
func disconnect() {
gesture?.removeTarget(self, action: #selector(didPan))
gesture = nil
}
Expand All @@ -171,8 +171,8 @@ class DragProducer: Subscription {
let pan = UIPanGestureRecognizer()
view.addGestureRecognizer(pan)

let dragStream = ValueObservable<DragProducer.Value> { observer in
return DragProducer(subscribedTo: pan, observer: observer).unsubscribe
let dragStream = ValueObservable<DragConnection.Value> { observer in
return DragConnection(subscribedTo: pan, observer: observer).disconnect
}
let subscription = dragStream.subscribe {
dump($0.state)
Expand All @@ -192,7 +192,7 @@ class <#Name#>Producer: Subscription {
### Step 2: Define the Value type

```swift
class DragProducer: Subscription {
class DragConnection: Subscription {
typealias Value = (state: UIGestureRecognizerState, location: CGPoint)
}
```
Expand Down Expand Up @@ -229,12 +229,12 @@ Your initializer must accept and store an `ValueObserver<Value>` instance.
}
```

### Step 5: Implement unsubscribe
### Step 5: Implement disconnect

You are responsible for disconnecting from and releasing any resources here.

```swift
func unsubscribe() {
func disconnect() {
gesture?.removeTarget(self, action: #selector(didPan))
gesture = nil
}
Expand All @@ -256,8 +256,8 @@ It often is helpful to provide the observer with the current state on registrati
### Step 7: Observe the producer

```swift
let dragStream = ValueObservable<DragProducer.Value> { observer in
return DragProducer(subscribedTo: pan, observer: observer).unsubscribe
let dragStream = ValueObservable<DragConnection.Value> { observer in
return DragConnection(subscribedTo: pan, observer: observer).disconnect
}
let subscription = dragStream.subscribe {
dump($0)
Expand Down
6 changes: 3 additions & 3 deletions examples/DelegateObservableExample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import IndefiniteObservable
// UIPanGestureRecognizer in this example, but the Producer pattern can be used for any delegated
// type.

class DragProducer: Subscription {
class DragSource {
typealias Value = (state: UIGestureRecognizerState, location: CGPoint)

init(subscribedTo gesture: UIPanGestureRecognizer, observer: ValueObserver<Value>) {
Expand All @@ -42,7 +42,7 @@ class DragProducer: Subscription {
return (gesture.state, gesture.location(in: gesture.view!))
}

func unsubscribe() {
func disconnect() {
gesture?.removeTarget(self, action: #selector(didPan))
gesture = nil
}
Expand All @@ -68,7 +68,7 @@ public class DelegateObservableExampleViewController: UIViewController {
view.addGestureRecognizer(pan)

let dragStream = IndefiniteObservable { observer in
return DragProducer(subscribedTo: pan, observer: observer).unsubscribe
return DragSource(subscribedTo: pan, observer: observer).disconnect
}

// Must hold a reference to the subscription, otherwise the stream will be deallocated when the
Expand Down
6 changes: 3 additions & 3 deletions examples/OperatorExample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,14 @@ public class OperatorExampleViewController: UIViewController {
let pan = UIPanGestureRecognizer()
view.addGestureRecognizer(pan)

let dragStream = ValueObservable<DragProducer.Value> { observer in
return DragProducer(subscribedTo: pan, observer: observer).unsubscribe
let dragStream = ValueObservable<DragSource.Value> { observer in
return DragSource(subscribedTo: pan, observer: observer).disconnect
}

let motionStream = MotionObservable<Int> { observer in
observer.next(5)
observer.state(.atRest)
return noopUnsubscription
return noopDisconnect
}

// Note that we avoid keeping a strong reference to self in the stream's operators.
Expand Down
71 changes: 27 additions & 44 deletions src/IndefiniteObservable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

let observable = IndefiniteObservable<Int> { observer in
observer.next(5)
return noopUnsubscription
return noopDisconnect
}

let subscription = observable.subscribe { value in
Expand All @@ -47,11 +47,11 @@
}
*/
open class IndefiniteObservable<O: Observer> {
public typealias Subscriber<O> = (O) -> (() -> Void)?
public typealias Connect<O> = (O) -> Disconnect

/** A subscriber is only invoked when subscribe is invoked. */
public init(_ subscriber: @escaping Subscriber<O>) {
self.subscriber = subscriber
/** Connect is only invoked when subscribe is invoked. */
public init(_ connect: @escaping Connect<O>) {
self.connect = connect
}

/**
Expand All @@ -66,14 +66,10 @@ open class IndefiniteObservable<O: Observer> {
- Returns: A subscription.
*/
public final func subscribe(observer: O) -> Subscription {
if let subscription = subscriber(observer) {
return SimpleSubscription(subscription)
} else {
return SimpleSubscription()
}
return Subscription(connect(observer))
}

private let subscriber: Subscriber<O>
private let connect: Connect<O>
}

/** An Observer is provided to an Observable's subscribe method. */
Expand All @@ -82,48 +78,35 @@ public protocol Observer {
var next: (Value) -> Void { get }
}

public typealias Disconnect = () -> Void

/** A Subscription is returned by IndefiniteObservable.subscribe. */
public protocol Subscription {
func unsubscribe()
public final class Subscription {
deinit {
unsubscribe()
}

init(_ disconnect: @escaping () -> Void) {
self.disconnect = disconnect
}

public func unsubscribe() {
disconnect?()
disconnect = nil
}

private var disconnect: (Disconnect)?
}

/**
A no-op subscription that can be returned by subscribers when there is no need for teardown.

Does nothing when unsubscribe is invoked.
A no-op disconnect block that can be returned by connectors when there is no need for teardown.

Example:

let observable = IndefiniteObservable<Int> { observer in
observer.next(5)

return noopUnsubscription
return noopDisconnect
}
*/
public let noopUnsubscription: (() -> Void)? = nil

// MARK: Private

// Internal class for ensuring that an active subscription keeps its stream alive.
// Streams don't hold strong references down the chain, so our subscriptions hold strong references
// "up" the chain to the IndefiniteObservable type.
private final class SimpleSubscription: Subscription {
deinit {
unsubscribe()
}

init(_ unsubscribe: @escaping () -> Void) {
_unsubscribe = unsubscribe
}

init() {
_unsubscribe = nil
}

func unsubscribe() {
_unsubscribe?()
_unsubscribe = nil
}

private var _unsubscribe: (() -> Void)?
}
public let noopDisconnect: Disconnect = { }
12 changes: 6 additions & 6 deletions tests/unit/MemoryLeakTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class MemoryLeakTests: XCTestCase {
func testObservableIsDeallocated() {
var observable: ValueObservable<CGFloat>? = ValueObservable<CGFloat> { observer in
observer.next(5)
return noopUnsubscription
return noopDisconnect
}
weak var weakObservable = observable

Expand All @@ -39,7 +39,7 @@ class MemoryLeakTests: XCTestCase {
func testDownstreamObservableKeepsUpstreamAlive() {
var observable: ValueObservable<CGFloat>? = ValueObservable<CGFloat> { observer in
observer.next(5)
return noopUnsubscription
return noopDisconnect
}
weak var weakObservable = observable

Expand All @@ -58,7 +58,7 @@ class MemoryLeakTests: XCTestCase {
func testSubscribedObservableIsDeallocated() {
var observable: ValueObservable<CGFloat>? = ValueObservable<CGFloat> { observer in
observer.next(5)
return noopUnsubscription
return noopDisconnect
}
weak var weakObservable = observable

Expand All @@ -78,7 +78,7 @@ class MemoryLeakTests: XCTestCase {
func testSubscribedObservableWithOperatorIsDeallocated() {
var observable: ValueObservable<CGFloat>? = ValueObservable<CGFloat> { observer in
observer.next(5)
return noopUnsubscription
return noopDisconnect
}
weak var weakObservable = observable

Expand All @@ -102,7 +102,7 @@ class MemoryLeakTests: XCTestCase {
autoreleasepool {
let observable: ValueObservable<CGFloat>? = ValueObservable<CGFloat> { observer in
observer.next(5)
return noopUnsubscription
return noopDisconnect
}
weakObservable = observable

Expand All @@ -128,7 +128,7 @@ class MemoryLeakTests: XCTestCase {
let value = 10
let observable = ValueObservable<Int> { observer in
observer.next(value)
return noopUnsubscription
return noopDisconnect
}
weakObservable = observable

Expand Down
Loading

0 comments on commit 43420ec

Please sign in to comment.