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

Commit 051b806

Browse files
author
Jeff Verkoeyen
committed
Merge branch 'release-candidate' into stable
2 parents 7280662 + 5373c05 commit 051b806

File tree

9 files changed

+126
-59
lines changed

9 files changed

+126
-59
lines changed

.swift-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.0

CHANGELOG.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,56 @@
1+
# 4.0.0
2+
3+
This major release improves the general usage of the observable pattern by removing the
4+
auto-unsubscription behavior from the Subscription type on deallocation.
5+
6+
```swift
7+
// Pre-4.0 usage required that you held on to the Subscription instance in order to continue
8+
// receiving values
9+
let subscription = observable.subscribe { value in
10+
print(value)
11+
}
12+
13+
// 4.0 allows you to subscribe to streams without holding on to the Subscription instance.
14+
observable.subscribe { value in
15+
print(value)
16+
}
17+
```
18+
19+
## Breaking changes
20+
21+
• Subscriptions no longer unsubscribe automatically upon deallocation.
22+
23+
This means that you no longer need to hold on to a Subscription in order to continue receiving
24+
values from a subscribed stream.
25+
26+
If you were previously depending on this behavior then you must now ensure that you explicitly
27+
unsubscribe from subscriptions.
28+
29+
## Source changes
30+
31+
* [[breaking] Remove automatic unsubscription from the Subscription object.](https://github.com/material-motion/indefinite-observable-swift/commit/8f5b89cf26bb4e0d26eca254419741278a290ec8) (Jeff Verkoeyen)
32+
33+
## API changes
34+
35+
Auto-generated by running:
36+
37+
apidiff origin/stable release-candidate swift IndefiniteObservable.xcworkspace IndefiniteObservable
38+
39+
## Subscription
40+
41+
## IndefiniteObservable
42+
43+
*modified* method: `subscribe(observer:)` in `IndefiniteObservable`
44+
45+
| Type of change: | key.attributes |
46+
|---|---|
47+
| From: | `( { "key.attribute" = "source.decl.attribute.final"; } )` |
48+
| To: | `( { "key.attribute" = "source.decl.attribute.final"; }, { "key.attribute" = "source.decl.attribute.discardableResult"; } )` |
49+
50+
## Non-source changes
51+
52+
* [Update README with new documentation for subscriptions.](https://github.com/material-motion/indefinite-observable-swift/commit/a492076d65aa9540737ac845fb1380c46cf53a9b) (Jeff Verkoeyen)
53+
154
# 3.1.0
255

356
## New features

IndefiniteObservable.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Pod::Spec.new do |s|
22
s.name = "IndefiniteObservable"
33
s.summary = "IndefiniteObservable is a minimal implementation of Observable with no concept of completion or failure."
4-
s.version = "3.1.0"
4+
s.version = "4.0.0"
55
s.authors = "The Material Motion Authors"
66
s.license = "Apache 2.0"
77
s.homepage = "https://github.com/material-motion/indefinite-observable-swift"

Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
PODS:
22
- CatalogByConvention (2.0.0)
3-
- IndefiniteObservable/examples (3.1.0):
3+
- IndefiniteObservable/examples (4.0.0):
44
- IndefiniteObservable/lib
5-
- IndefiniteObservable/lib (3.1.0)
5+
- IndefiniteObservable/lib (4.0.0)
66

77
DEPENDENCIES:
88
- CatalogByConvention
@@ -15,8 +15,8 @@ EXTERNAL SOURCES:
1515

1616
SPEC CHECKSUMS:
1717
CatalogByConvention: be55c2263132e4f9f59299ac8a528ee8715b3275
18-
IndefiniteObservable: 8dd88933eb00b0186cdcc665692beba53da34f44
18+
IndefiniteObservable: 0e948393173ea7dc68a9f439530559aa84d96d4c
1919

2020
PODFILE CHECKSUM: 3e4cdba95901e07a289159f0c5a8b830ecb1a5c8
2121

22-
COCOAPODS: 1.1.1
22+
COCOAPODS: 1.2.0

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,20 +112,22 @@ let observable = ValueObservable<<#ValueType#>> { observer in
112112

113113
## How to subscribe to a stream
114114

115-
Streams are kept in memory by their subscriptions.
116-
117115
```swift
118-
let subscription = observable.subscribe { value in
116+
observable.subscribe { value in
119117
print(value)
120118
}
121119
```
122120

123121
## How to unsubscribe from a stream
124122

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

128126
```swift
127+
let subscription = observable.subscribe { value in
128+
print(value)
129+
}
130+
129131
subscription.unsubscribe()
130132
```
131133

src/IndefiniteObservable.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,18 @@ open class IndefiniteObservable<O: Observer> {
5757
/**
5858
Subscribes to the IndefiniteObservable.
5959

60-
The returned subscription will hold a strong reference to the IndefiniteObservable chain. The
61-
reference can be released by calling unsubscribe on the returned subscription. The Subscription
62-
is type-erased, making it possible to keep a collection of Subscription objects for as long as
63-
you need the associated streams alive.
60+
The returned subscription will hold a strong reference to the disconnect chain. The reference can
61+
be released by calling unsubscribe on the returned subscription. The Subscription is type-erased,
62+
making it possible to keep a collection of Subscription objects for as long as you need the
63+
associated streams alive.
64+
65+
If you do not keep the returned subscription then the disconnect calls will never be invoked for
66+
this observer.
6467

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

8387
/** A Subscription is returned by IndefiniteObservable.subscribe. */
8488
public final class Subscription {
85-
deinit {
86-
unsubscribe()
87-
}
88-
8989
public init(_ disconnect: @escaping () -> Void) {
9090
self.disconnect = disconnect
9191
}

tests/unit/MemoryLeakTests.swift

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class MemoryLeakTests: XCTestCase {
6363
weak var weakObservable = observable
6464

6565
autoreleasepool {
66-
let _ = observable!.subscribe {
66+
observable!.subscribe {
6767
let _ = $0
6868
}
6969
// Remove our only strong reference.
@@ -83,7 +83,7 @@ class MemoryLeakTests: XCTestCase {
8383
weak var weakObservable = observable
8484

8585
autoreleasepool {
86-
let _ = observable!.map { value in
86+
observable!.map { value in
8787
return value * value
8888
}.subscribe {
8989
let _ = $0
@@ -97,7 +97,7 @@ class MemoryLeakTests: XCTestCase {
9797
XCTAssertNil(weakObservable)
9898
}
9999

100-
func testUnsubscribedObservableWithOperatorIsDeallocated() {
100+
func testObservableWithOperatorIsDeallocated() {
101101
weak var weakObservable: ValueObservable<CGFloat>?
102102
autoreleasepool {
103103
let observable: ValueObservable<CGFloat>? = ValueObservable<CGFloat> { observer in
@@ -106,13 +106,11 @@ class MemoryLeakTests: XCTestCase {
106106
}
107107
weakObservable = observable
108108

109-
let subscription = observable!.map { value in
109+
observable!.map { value in
110110
return value * value
111-
}.subscribe {
112-
let _ = $0
113-
}
114-
// Remove our only strong reference.
115-
subscription.unsubscribe()
111+
}.subscribe {
112+
let _ = $0
113+
}
116114
}
117115

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

123121
func testSubscriptionDoesNotKeepObservableInMemory() {
124122
weak var weakObservable: ValueObservable<Int>?
125-
var subscription: Subscription?
126123

127124
autoreleasepool {
128125
let value = 10
@@ -132,11 +129,34 @@ class MemoryLeakTests: XCTestCase {
132129
}
133130
weakObservable = observable
134131

135-
subscription = observable.subscribe { _ in }
132+
observable.subscribe { _ in }
136133
}
137134

138135
XCTAssertNil(weakObservable)
136+
}
137+
138+
func testSubscriptionDoesNotKeepObserverInMemory() {
139+
weak var weakObserver: ValueObserver<Int>?
140+
141+
var didDisconnect = false
142+
var didSetObserver = false
143+
144+
autoreleasepool {
145+
let value = 10
146+
let observable = ValueObservable<Int> { observer in
147+
weakObserver = observer
148+
didSetObserver = true
149+
observer.next(value)
150+
return {
151+
didDisconnect = true
152+
}
153+
}
154+
155+
observable.subscribe { _ in }
156+
}
139157

140-
subscription?.unsubscribe()
158+
XCTAssertNil(weakObserver)
159+
XCTAssertTrue(didSetObserver)
160+
XCTAssertFalse(didDisconnect)
141161
}
142162
}

0 commit comments

Comments
 (0)