Skip to content

Releases: ReactiveCocoa/ReactiveSwift

3.1.0

11 Jan 20:05
Compare
Choose a tag to compare

This is the first release of ReactiveSwift 3.1. It supports Swift 3.2 and Swift 4.0.

Bugfix

  1. Fixed a scenario of downstream interruptions being dropped. (#577, kudos to @andersio)

    Manual interruption of time shifted producers, including delay, observe(on:), throttle, debounce and lazyMap, should discard outstanding events at best effort ASAP.

    But in ReactiveSwift 2.0 to 3.0, the manual interruption is ignored if the upstream producer has terminated. For example:

    // Completed upstream + `delay`.
    SignalProducer.empty
        .delay(10.0, on: QueueScheduler.main)
        .startWithCompleted { print("The producer should have been interrupted!") }
        .dispose()
    
    // Console(t+10): The producer should have been interrupted!

    The expected behavior has now been restored.

    Please note that, since ReactiveSwift 2.0, while the interruption is handled immediately, the interrupted event delivery is not synchronous — it generally respects the closest asynchronous operator applied, and delivers on that scheduler.

  2. Fixed schedule(after:interval:leeway:) being cancelled when the returned Disposable is not retained. (#584, kudos to @jjoelson)

    The issue affects only direct use of QueueScheduler. SignalProducer.timer is not affected.

Addition

  1. SignalProducer.concat now has an overload that accepts an error. (#564, kudos to @nmccann)

Change

  1. Fix some documentation errors (#560, kudos to @ikesyo)

3.1 Release Candidate 1

22 Dec 18:38
Compare
Choose a tag to compare
Pre-release

This is the first release candidate of ReactiveSwift 3.1. It supports Swift 3.2 and Swift 4.0.

Bugfix

  1. Fixed a scenario of downstream interruptions being dropped. (#577, kudos to @andersio)

    Manual interruption of time shifted producers, including delay, observe(on:), throttle, debounce and lazyMap, should discard outstanding events at best effort ASAP.

    But in ReactiveSwift 2.0 to 3.0, the manual interruption is ignored if the upstream producer has terminated. For example:

    // Completed upstream + `delay`.
    SignalProducer.empty
        .delay(10.0, on: QueueScheduler.main)
        .startWithCompleted { print("Value should have been discarded!") }
        .dispose()
    
    // Console(t+10): Value should have been discarded!

    The expected behavior has now been restored.

    Please note that, since ReactiveSwift 2.0, while the interruption is handled immediately, the interrupted event delivery is not synchronous — it generally respects the closest asynchronous operator applied, and delivers on that scheduler.

Addition

  1. SignalProducer.concat now has an overload that accepts an error. (#564, kudos to @nmccann)

Change

  1. Fix some documentation errors (#560, kudos to @ikesyo)

3.0.0

17 Nov 04:02
Compare
Choose a tag to compare

This is the first release of ReactiveSwift 3.0. It supports Swift 3.2 and Swift 4.0, and requires either Xcode 9 or the Swift 4.0 OSS compiler toolchain.

Breaking Change

  1. Signal now uses Lifetime for resource management. (#404, kudos to @andersio)

    The Signal initialzer now accepts a generator closure that is passed with the input Observer and the Lifetime as its arguments. The original variant accepting a single-argument generator closure is now obselete. This is a source breaking change.

    // New: Add `Disposable`s to the `Lifetime`.
    let candies = Signal<U, E> { (observer: Signal<U, E>.Observer, lifetime: Lifetime) in
       lifetime += trickOrTreat.observe(observer)
    }
    
    // Obsolete: Returning a `Disposable`.
    let candies = Signal { (observer: Signal<U, E>.Observer) -> Disposable? in
       return trickOrTreat.observe(observer)
    }
  2. Unavailable symbols in ReactiveSwift 2.x have been removed. (#551, kudos to @ikesyo)

Bugfix

  1. Workaround an unexpected EGAGIN error being returned by pthread in 32-bit ARM debug builds. (#508)

  2. Fixed a potential integer overflow regarding the leeway computation in SignalProducer.timer. (#506, kudos to @soxjke)

Changes

  1. ReactiveSwift has been migrated to Swift 4.0. (#422, kudos to @andersio)

  2. SignalProducer.startWithSignal now returns the value of the setup closure. (#533, kudos to @Burgestrand)

  3. Disabled code coverage data to allow app submissions with Xcode 9.0 (see Carthage/Carthage#2056, kudos to @NachoSoto)

  4. The SignalProducer internals have undergone a significant refactoring, which bootstraps the effort to reduce the overhead of constant producers and producer compositions. (#487, kudos to @andersio)

Deprecation

  1. Signal.Observer.action has been deprecated. Use Signal.Observer.send instead. (#515)

3.0.0 Release Candidate 1

30 Oct 05:34
Compare
Choose a tag to compare
Pre-release

This is the first release candidate of ReactiveSwift 3.0. It supports both Swift 3.2 and Swift 4.0.

Bugfix

  1. Fixed a potential integer overflow regarding the leeway computation in SignalProducer.timer. (#506, kudos to @soxjke)

3.0.0 alpha 1

19 Oct 17:55
Compare
Choose a tag to compare
3.0.0 alpha 1 Pre-release
Pre-release

This is the first alpha release of ReactiveSwift 3.0. It supports Swift 3.2 and Swift 4.0, and requires either Xcode 9 or the Swift 4.0 OSS compiler toolchain.

Breaking Change

  1. Signal now uses Lifetime for resource management. (#404, kudos to @andersio)

    The Signal initialzer now accepts a generator closure that is passed with the input Observer and the Lifetime as its arguments. The original variant accepting a single-argument generator closure is now obselete. This is a source breaking change.

    // New: Add `Disposable`s to the `Lifetime`.
    let candies = Signal<U, E> { (observer: Signal<U, E>.Observer, lifetime: Lifetime) in
       lifetime += trickOrTreat.observe(observer)
    }
    
    // Obsolete: Returning a `Disposable`.
    let candies = Signal { (observer: Signal<U, E>.Observer) -> Disposable? in
       return trickOrTreat.observe(observer)
    }

Bugfix

  1. Workaround an unexpected EGAGIN error being returned by pthread in 32-bit ARM debug builds. (#508)

Changes

  1. ReactiveSwift has been migrated to Swift 4.0. (#422, kudos to @andersio)

  2. SignalProducer.startWithSignal now returns the value of the setup closure. (#533, kudos to @Burgestrand)

  3. Disabled code coverage data to allow app submissions with Xcode 9.0 (see Carthage/Carthage#2056, kudos to @NachoSoto)

  4. The SignalProducer internals have undergone a significant refactoring, which bootstraps the effort to reduce the overhead of constant producers and producer compositions. (#487, kudos to @andersio)

Deprecation

  1. Signal.Observer.action has been deprecated. Use Signal.Observer.send instead. (#515)

2.1.0 alpha 2

16 Sep 09:48
Compare
Choose a tag to compare
2.1.0 alpha 2 Pre-release
Pre-release

This is the second alpha release of ReactiveSwift 2.1. It targets Swift 3.1, Swift 3.2 and Swift 4.0.

Changes

  1. Disabled code coverage data to allow app submissions with Xcode 9.0 (see Carthage/Carthage#2056, kudos to @NachoSoto)

2.1.0 alpha 1

15 Sep 17:50
Compare
Choose a tag to compare
2.1.0 alpha 1 Pre-release
Pre-release

This is the first alpha release of ReactiveSwift 2.1. It targets Swift 3.1, Swift 3.2 and Swift 4.0.

Changes

  1. Signal.Observer.action has been deprecated. Use Signal.Observer.send instead. (#515)

  2. Workaround an unexpected EGAGIN error being returned by pthread in 32-bit ARM debug builds. (#508)

  3. The SignalProducer internals have undergone a significant refactoring, which bootstraps the effort to reduce the overhead of constant producers and producer compositions. (#487, kudos to @andersio)

2.0.1

16 Aug 04:04
Compare
Choose a tag to compare

This is the first maintenance release of ReactiveSwift 2.0. It targets Swift 3.1 with preliminary support of Swift 3.2 and Swift 4.0.

Change

  1. Addressed the exceptionally high build time. (#495)

Addition

  1. New operator retry(upTo:interval:on:). (#496, kudos to @tjnet)

    This delays retrying on failure by interval until hitting the upTo limitation.

2.0.0

21 Jul 16:00
Compare
Choose a tag to compare

This is the second major release of ReactiveSwift. It requires Swift 3.1 (Xcode 8.3.x), and preliminarily supports Swift 3.2 and Swift 4.0 (Xcode 9).

Highlights

Safer Signal lifetime semantics (#355, #463)

The Signal lifetime semantics have been updated to improve interoperability with memory debugging tools. ReactiveSwift 2.0 adopted a new Signal internal which does not exploit deliberate retain cycles that consequentially confuse memory debugging tools.

ReactiveSwift 2.0 automatically terminates a Signal, when:

  1. (New) its input observer of the Signal is not retained; OR
  2. the Signal is neither retained nor has any active observer;

Input observer refers to the Observer the Signal receives input from. It is created and passed to you by Signal.init, Signal.pipe and SignalProducer.init.

Specifically, when an input Observer deinitializes, semantically it implies the associated Signal has no further event to be delivered. So ReactiveSwift would now interrupt the Signal automatically, and release all the associated resources.

func scopedSignal() -> Signal<Never, NoError> {
    // Note that the `Observer` is discarded immediately.
    let (signal, _) = Signal<Never, NoError>.pipe()
    return signal
}

var isInterrupted = false

withExtendedLifetime(scopedSignal()) { signal in
    signal.observeInterrupted { isInterrupted = true }

    // ReactiveSwift 1.x:
    // The `Signal` is still alive, probably forever unless the observer is detached.
    expect(isInterrupted) == false

    // ReactiveSwift 2.0:
    // The `Signal` is automatically interrupted, since the deinitialization of the
    // input `Observer` implies no further event would be sent.
    expect(isInterrupted) == true
}

Similarly for a deinitialised Signal, since no further observation can ever be made, ReactiveSwift would dispose of it when it knows for certain it has no active observer. Note that this is already the case for ReactiveSwift 1.x.

let checkpoint = AnyDisposable()
let escaped = CompositeDisposable()

func scopedObserver() -> Signal<Never, NoError>.Observer {
    // Note that the `Signal` does not escape the scope.
    let (signal, observer) = Signal<Never, NoError>.pipe(disposable: checkpoint)
    escaped += signal.observe(Observer())
    return observer
}

withExtendedLifetime(scopedObserver()) {
    escaped.dispose()

    // ReactiveSwift 1.x and 2.0:
    // Since no further observation can be made to the `Signal`, and it has no
    // active observer at this point, the `Signal` is automatically disposed of.
    expect(checkpoint.isDisposed) == true
}

In short, the Signal terminates when either of its ends implicitly declare their lack of interest — derived from the deinitialization of the Signal or the input Observer — to send or receive events. This makes ReactiveSwift more ARC friendly than before.

It is expected that memory debugging tools would no longer report irrelevant negative leaks that were once caused by the ReactiveSwift internals.

SignalProducer resource management (#334)

SignalProducer now uses Lifetime for resource management. You may observe the Lifetime for the disposal of the produced Signal. You may also continue to use the += convenience on Lifetime for adding Disposables.

let producer = SignalProducer<Int, NoError> { observer, lifetime in
    lifetime += numbers.observe(observer)
}

If you need to interrupt the SignalProducer, you should now do it through the input Observer:

let producer = SignalProducer<Int, NoError> { observer, _ in
    observer.sendInterrupted()
}

Reduced overhead for all SignalProducer lifted operators. (#140)

All SignalProducer lifted operators no longer yield an extra Signal. As a result, the cost of event delivery has been considerably reduced, and SignalProducer is generally as performant as Signal.

N-ary SignalProducer operators with generic operands (#410)

N-ary SignalProducer operators are now generic and accept any type that can be expressed as SignalProducer. Types may conform to SignalProducerConvertible to become an eligible operand.

For example:

let property = MutableProperty<Int>(0)
let producer = SignalProducer<Int, NoError>.never
let signal = Signal<Int, NoError>.never

/// Valid in ReactiveSwift 2.0.
_ = SignalProducer.combineLatest(property, producer, signal)

Changes

Signal and SignalProducer

  1. All Signal and SignalProducer operators now belongs to the respective concrete types. (#304)

    Custom operators should extend the concrete types directly. SignalProtocol and SignalProducerProtocol should be used only for constraining associated types.

  2. combineLatest and zip are optimised to have a constant overhead regardless of arity, mitigating the possibility of stack overflow. (#345, #471, kudos to @stevebrambilla for catching a bug in the implementation)

  3. When composing Signal and SignalProducer of inhabitable types, e.g. Never or NoError, ReactiveSwift now warns about operators that are illogical to use, and traps at runtime when such operators attempt to instantiate an instance. (#429, kudos to @andersio)

  4. interrupted now respects observe(on:). (#140)

    When a SignalProducer is interrupted, if observe(on:) is the last applied operator, interrupted would now be delivered on the Scheduler passed to observe(on:) just like other events.

  5. flatMap(_:transform:) is renamed to flatMap(_:_:). (#339)

  6. promoteErrors(_:)is renamed to promoteError(_:). (#408)

  7. Event is renamed to Signal.Event. (#376)

  8. Observer is renamed to Signal.Observer. (#376)

Action

  1. Action(input:_:), Action(_:), Action(enabledIf:_:) and Action(state:enabledIf:_:) are renamed to Action(state:execute:), Action(execute:), Action(enabledIf:execute:) and Action(state:enabledIf:execute:) respectively. (#325)

  2. Feedbacks from isEnabled and isExecuting to the state of the same Action, including all enabledIf convenience initializers, no longer deadlocks. (#400, kudos to @andersio)

    Note that legitimate feedback loops would still deadlock.

  3. Added new convenience initialisers to Action that make creating actions with state input properties easier. When creating an Action that is conditionally enabled based on an optional property, use the renamed Action.init(unwrapping:execute:) initialisers. (#455, kudos to @sharplet)

Properties

  1. The memory overhead of property composition has been considerably reduced. (#340)

  2. MutableProperty now enforces exclusivity of access. (#419, kudos to @andersio)

    In other words, nested modification in MutableProperty.modify is now prohibited. Generally speaking, it should have extremely limited impact as in most cases the MutableProperty would have been deadlocked already.

  3. ValidationResult and ValidatorOutput have been renamed to ValidatingProperty.Result and ValidatingProperty.Decision, respectively. (#443)

Bindings

  1. The BindingSource now requires only a producer representation of self. (#359)

  2. The <~ operator overloads are now provided by BindingTargetProvider. (#359)

Disposables

  1. SimpleDisposable and ActionDisposable has been folded into AnyDisposable. (#412)

  2. CompositeDisposable.DisposableHandle is replaced by Disposable?. (#363)

  3. The += operator overloads for CompositeDisposable are now hosted inside the concrete types. (#412)

Bag

  1. Improved the performance of Bag. (#354)

  2. RemovalToken is renamed to Bag.Token. (#354)

Schedulers

  1. Scheduler gains a class bound. (#333)

Lifetime

  1. Lifetime.ended now uses the inhabitable Never as its value type. (#392)

Atomic

  1. Signal and Atomic now use os_unfair_lock when it is available. (#342)

Additions

  1. FlattenStrategy.race is introduced. (#233, kudos to @inamiy)

    race flattens whichever inner signal that first sends an event, and ignores the rest.

  2. FlattenStrategy.concurrent is introduced. (#298, kudos to @andersio)

    concurrent starts and flattens inner signals according to the specified concurrency limit. If an inner signal is received after the limit is reached, it would be queued and drained later as the in-flight inner signals terminate.

  3. New operators: reduce(into:) and scan(into:). (#365, kudos to @ikesyo)

    These variants pass to the closure an inout reference to the accumulator, which helps the performance when a large value type is used, e.g. collection.

  4. combinePrevious for Signal and SignalProducer no longer requires an initial value. The first tuple would be emitted as soon as the second value is received by the operator if no initial value is given. (#445, kudos to @andersio)

  5. New operator: promoteValue. (#429)

  6. promoteError can now infer the new error type from the context. (#413, kudos to @andersio)

  7. Property(initial:then:) gains overloads that accept a producer or signal of the wrapped value type when the value type is an Optional. (#396)

Swift 3.2+

  1. In Swift 3.2 or later, you can use map() with the new Smart Key Paths. (#435, kudos to @sharplet)

  2. In Swift 3.2 or later, you may create BindingTarget for a key path of a specific object. (#440, kudos to @andersio)

Deprecations and Removals

  1. The requirement BindingSource.observe(_:during:) and the implementations have been removed.

  2. All Swift 2 (ReactiveCocoa 4) obsolete symbols have been removed.

  3. All deprecated methods and protocols in ReactiveSwift 1.1.x are no longer available.

Bugfixes

  1. Fixed an impedance mismatch in the Signal internals that caused heap co...
Read more

2.0.0 Release Candidate 3

12 Jul 07:53
Compare
Choose a tag to compare
Pre-release

This is the third release candidate of ReactiveSwift 2.0. It targets Swift 3.1 with preliminary support of Swift 3.2 and Swift 4.0.

Changes

  1. Updated for Swift 4.0 shipped with Xcode 9 beta 3. (#484, kudos to @stephencelis)

  2. Lifetime.+= which ties a Disposable to a Lifetime, is now part of the public API and is no longer deprecated.

  3. Feedbacks from isEnabled to the state of the same Action no longer deadlocks if it does not constitute an infinite feedback loop. (#481, kudos to @andersio)

    Note that isExecuting already supports Action state feedback, and legitimate feedback loops would still deadlock.