Skip to content

Commit

Permalink
Merge pull request #229 from ReactiveCocoa/lifetime-api
Browse files Browse the repository at this point in the history
`Lifetime.observeEnded` and `take(during:)` fix.
  • Loading branch information
andersio authored Feb 18, 2017
2 parents 6d73db3 + 3081538 commit ea33db2
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Sources/Action.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public final class Action<Input, Output, Error: Swift.Error> {
lifetime = Lifetime(deinitToken)

// Retain the `property` for the created `Action`.
lifetime.ended.observeCompleted { _ = property }
lifetime.observeEnded { _ = property }

executeClosure = { state, input in execute(state as! State.Value, input) }

Expand Down
21 changes: 20 additions & 1 deletion Sources/Lifetime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ public final class Lifetime {
/// MARK: Instance properties

/// A signal that sends a `completed` event when the lifetime ends.
public let ended: Signal<(), NoError>
///
/// - note: Consider using `Lifetime.observeEnded` if only a closure observer
/// is to be attached.
public let ended: Signal<(), NoError>

/// MARK: Initializers

Expand All @@ -45,6 +48,22 @@ public final class Lifetime {
self.init(ended: token.ended)
}

/// Observe the termination of `self`.
///
/// - parameters:
/// - action: The action to be invoked when `self` ends.
///
/// - returns: A disposable that detaches `action` from the lifetime, or `nil`
/// if `lifetime` has already ended.
@discardableResult
public func observeEnded(_ action: @escaping () -> Void) -> Disposable? {
return ended.observe { event in
if event.isTerminating {
action()
}
}
}

/// A token object which completes its signal when it deinitializes.
///
/// It is generally used in conjuncion with `Lifetime` as a private
Expand Down
7 changes: 6 additions & 1 deletion Sources/Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1322,7 +1322,12 @@ extension SignalProtocol {
///
/// - returns: A signal that will deliver events until `lifetime` ends.
public func take(during lifetime: Lifetime) -> Signal<Value, Error> {
return take(until: lifetime.ended)
return Signal { observer in
let disposable = CompositeDisposable()
disposable += self.observe(observer)
disposable += lifetime.observeEnded(observer.sendCompleted)
return disposable
}
}

/// Forward events from `self` until `trigger` sends a `value` or
Expand Down
2 changes: 1 addition & 1 deletion Sources/SignalProducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ extension SignalProducerProtocol {
///
/// - returns: A producer that will deliver events until `lifetime` ends.
public func take(during lifetime: Lifetime) -> SignalProducer<Value, Error> {
return take(until: lifetime.ended)
return lift { $0.take(during: lifetime) }
}

/// Forward events from `self` until `trigger` sends a `value` or `completed`
Expand Down
38 changes: 35 additions & 3 deletions Tests/ReactiveSwiftTests/LifetimeSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,45 @@ final class LifetimeSpec: QuickSpec {

(lifetime, token) = Lifetime.makeLifetime()

var isCompleted = false
lifetime.ended.observeCompleted { isCompleted = true }
var isEnded = false
lifetime.observeEnded { isEnded = true }

token = Lifetime.Token()
_ = token

expect(isCompleted) == true
expect(isEnded) == true
}

it("should notify its observers when the underlying token deinitializes") {
let object = MutableReference(TestObject())

var isEnded = false

object.value!.lifetime.observeEnded { isEnded = true }
expect(isEnded) == false

object.value = nil
expect(isEnded) == true
}

it("should notify its observers of the deinitialization of the underlying token even if the `Lifetime` object is retained") {
let object = MutableReference(TestObject())
let lifetime = object.value!.lifetime

var isEnded = false

lifetime.observeEnded { isEnded = true }
expect(isEnded) == false

object.value = nil
expect(isEnded) == true
}

it("should notify its observers of its deinitialization if it has already ended") {
var isEnded = false

Lifetime.empty.observeEnded { isEnded = true }
expect(isEnded) == true
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/ReactiveSwiftTests/PropertySpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ class PropertySpec: QuickSpec {
var property = Optional(MutableProperty<Int>(1))

var isEnded = false
property!.lifetime.ended.observeCompleted {
property!.lifetime.observeEnded {
isEnded = true
}

Expand Down
2 changes: 1 addition & 1 deletion Tests/ReactiveSwiftTests/SignalProducerSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ class SignalProducerSpec: QuickSpec {

it("doesn't extend the lifetime of the throttle property") {
var completed = false
shouldThrottle.lifetime.ended.observeCompleted { completed = true }
shouldThrottle.lifetime.observeEnded { completed = true }

observer.send(value: 1)
shouldThrottle = nil
Expand Down
2 changes: 1 addition & 1 deletion Tests/ReactiveSwiftTests/SignalSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1713,7 +1713,7 @@ class SignalSpec: QuickSpec {

it("doesn't extend the lifetime of the throttle property") {
var completed = false
shouldThrottle.lifetime.ended.observeCompleted { completed = true }
shouldThrottle.lifetime.observeEnded { completed = true }

observer.send(value: 1)
shouldThrottle = nil
Expand Down

0 comments on commit ea33db2

Please sign in to comment.