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 Apr 26, 2017
2 parents 7280662 + 5373c05 commit 051b806
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 59 deletions.
1 change: 1 addition & 0 deletions .swift-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.0
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
# 4.0.0

This major release improves the general usage of the observable pattern by removing the
auto-unsubscription behavior from the Subscription type on deallocation.

```swift
// Pre-4.0 usage required that you held on to the Subscription instance in order to continue
// receiving values
let subscription = observable.subscribe { value in
print(value)
}

// 4.0 allows you to subscribe to streams without holding on to the Subscription instance.
observable.subscribe { value in
print(value)
}
```

## Breaking changes

• Subscriptions no longer unsubscribe automatically upon deallocation.

This means that you no longer need to hold on to a Subscription in order to continue receiving
values from a subscribed stream.

If you were previously depending on this behavior then you must now ensure that you explicitly
unsubscribe from subscriptions.

## Source changes

* [[breaking] Remove automatic unsubscription from the Subscription object.](https://github.com/material-motion/indefinite-observable-swift/commit/8f5b89cf26bb4e0d26eca254419741278a290ec8) (Jeff Verkoeyen)

## API changes

Auto-generated by running:

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

## Subscription

## IndefiniteObservable

*modified* method: `subscribe(observer:)` in `IndefiniteObservable`

| Type of change: | key.attributes |
|---|---|
| From: | `( { "key.attribute" = "source.decl.attribute.final"; } )` |
| To: | `( { "key.attribute" = "source.decl.attribute.final"; }, { "key.attribute" = "source.decl.attribute.discardableResult"; } )` |

## Non-source changes

* [Update README with new documentation for subscriptions.](https://github.com/material-motion/indefinite-observable-swift/commit/a492076d65aa9540737ac845fb1380c46cf53a9b) (Jeff Verkoeyen)

# 3.1.0

## New features
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 = "3.1.0"
s.version = "4.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
8 changes: 4 additions & 4 deletions Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
PODS:
- CatalogByConvention (2.0.0)
- IndefiniteObservable/examples (3.1.0):
- IndefiniteObservable/examples (4.0.0):
- IndefiniteObservable/lib
- IndefiniteObservable/lib (3.1.0)
- IndefiniteObservable/lib (4.0.0)

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

SPEC CHECKSUMS:
CatalogByConvention: be55c2263132e4f9f59299ac8a528ee8715b3275
IndefiniteObservable: 8dd88933eb00b0186cdcc665692beba53da34f44
IndefiniteObservable: 0e948393173ea7dc68a9f439530559aa84d96d4c

PODFILE CHECKSUM: 3e4cdba95901e07a289159f0c5a8b830ecb1a5c8

COCOAPODS: 1.1.1
COCOAPODS: 1.2.0
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,20 +112,22 @@ let observable = ValueObservable<<#ValueType#>> { observer in

## How to subscribe to a stream

Streams are kept in memory by their subscriptions.

```swift
let subscription = observable.subscribe { value in
observable.subscribe { value in
print(value)
}
```

## How to unsubscribe from a stream

Unsubscribe from a stream to allow the stream to be released. The stream can be deallocated once all
of its subscriptions have unsubscribed.
Unsubscribing will invoke the observable's disconnect method. To unsubscribe, you must retain a
reference to the subscription instance returned by subscribe.

```swift
let subscription = observable.subscribe { value in
print(value)
}

subscription.unsubscribe()
```

Expand Down
16 changes: 8 additions & 8 deletions src/IndefiniteObservable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,18 @@ open class IndefiniteObservable<O: Observer> {
/**
Subscribes to the IndefiniteObservable.

The returned subscription will hold a strong reference to the IndefiniteObservable chain. The
reference can be released by calling unsubscribe on the returned subscription. The Subscription
is type-erased, making it possible to keep a collection of Subscription objects for as long as
you need the associated streams alive.
The returned subscription will hold a strong reference to the disconnect chain. The reference can
be released by calling unsubscribe on the returned subscription. The Subscription is type-erased,
making it possible to keep a collection of Subscription objects for as long as you need the
associated streams alive.

If you do not keep the returned subscription then the disconnect calls will never be invoked for
this observer.

- Parameter next: A block that will be executed when new values are sent from upstream.
- Returns: A subscription.
*/
@discardableResult
public final func subscribe(observer: O) -> Subscription {
return Subscription(connect(observer))
}
Expand All @@ -82,10 +86,6 @@ public typealias Disconnect = () -> Void

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

public init(_ disconnect: @escaping () -> Void) {
self.disconnect = disconnect
}
Expand Down
44 changes: 32 additions & 12 deletions tests/unit/MemoryLeakTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class MemoryLeakTests: XCTestCase {
weak var weakObservable = observable

autoreleasepool {
let _ = observable!.subscribe {
observable!.subscribe {
let _ = $0
}
// Remove our only strong reference.
Expand All @@ -83,7 +83,7 @@ class MemoryLeakTests: XCTestCase {
weak var weakObservable = observable

autoreleasepool {
let _ = observable!.map { value in
observable!.map { value in
return value * value
}.subscribe {
let _ = $0
Expand All @@ -97,7 +97,7 @@ class MemoryLeakTests: XCTestCase {
XCTAssertNil(weakObservable)
}

func testUnsubscribedObservableWithOperatorIsDeallocated() {
func testObservableWithOperatorIsDeallocated() {
weak var weakObservable: ValueObservable<CGFloat>?
autoreleasepool {
let observable: ValueObservable<CGFloat>? = ValueObservable<CGFloat> { observer in
Expand All @@ -106,13 +106,11 @@ class MemoryLeakTests: XCTestCase {
}
weakObservable = observable

let subscription = observable!.map { value in
observable!.map { value in
return value * value
}.subscribe {
let _ = $0
}
// Remove our only strong reference.
subscription.unsubscribe()
}.subscribe {
let _ = $0
}
}

// If this fails it means there's a retain cycle. Place a breakpoint here and use the Debug
Expand All @@ -122,7 +120,6 @@ class MemoryLeakTests: XCTestCase {

func testSubscriptionDoesNotKeepObservableInMemory() {
weak var weakObservable: ValueObservable<Int>?
var subscription: Subscription?

autoreleasepool {
let value = 10
Expand All @@ -132,11 +129,34 @@ class MemoryLeakTests: XCTestCase {
}
weakObservable = observable

subscription = observable.subscribe { _ in }
observable.subscribe { _ in }
}

XCTAssertNil(weakObservable)
}

func testSubscriptionDoesNotKeepObserverInMemory() {
weak var weakObserver: ValueObserver<Int>?

var didDisconnect = false
var didSetObserver = false

autoreleasepool {
let value = 10
let observable = ValueObservable<Int> { observer in
weakObserver = observer
didSetObserver = true
observer.next(value)
return {
didDisconnect = true
}
}

observable.subscribe { _ in }
}

subscription?.unsubscribe()
XCTAssertNil(weakObserver)
XCTAssertTrue(didSetObserver)
XCTAssertFalse(didDisconnect)
}
}
Loading

0 comments on commit 051b806

Please sign in to comment.