be notified on results of that work Publisher Subscriber Hey! I perform work and emit values as they change Cool, I’d like to receive these updated values!
composition becomes extremely powerful. Publisher all the things tap on a button @IBAction user/pass from UITextField(s) .text network request URLSession + Closure tap on a button Publisher user/pass from UITextField(s) Publisher network request Publisher Different interfaces Constant context switching and juggling Same interfaces Composition, unification
value from the source, letting you keep your app up to date. Reactive and Declarative let canMakePurchase = productPrice .combineLatest(userBalance) .map { $0 !" $1 }!# Publisher of Bool canMakePurchase ALWAYS reflects the latest state canMakePurchase doesn’t reflect the latest state var userBalance = 5 let productPrice = 10 let canMakePurchase = userBalance !" productPrice print(canMakePurchase) userBalance += 20 print(canMakePurchase) !# false !# STILL false!
with a completion event Any completion event terminates the publisher and no further values will be published 1 1 2 3 5 8 13 X Publisher<Int, MyError> 0 1 1 2 3 5 8 13 0 Completed with no error Completed with MyError , Publisher
one or more Subscribers based on their demand - Subscriber Type of values this subscriber receives Type of error this publisher emits Subscriber<Input, Failure: Error>
it using its cancel() method or deallocate the subscription . Subscription cancel() Canceling a subscription will release any memory and resources allocated by the attached subscriber Subscription X
specific amount of values it’s willing to handle 0 Backpressure Publisher Subscriber I’d like to receive up to X values Here are values! I’ll never give you more than you demand Based on these new values, I’d like to adjust my Demand 1 2 3 The publisher must respect the contract and only produce values up to the Subscriber’s demand Types of Demand: Demand.unlimited Demand.none Demand.max(x)
manually send values to, which is quite useful for bridging imperative code 4 Subject Publisher Subject Immutable (read-only) Values produced internally by the publisher Mutable (read/write) Values can be manually pushed by consumer
4 Subject PassthroughSubject Useful for representing events let tappedButton = PassthroughSubject<Void, Never>() // Send a “tap” to the Subject, existing subscribers // will immediately receive it tappedButton.send() New subscribers get any values sent after the subscription
4 Subject CurrentValueSubject Useful for representing state // Takes an initial value let isLoading = CurrentValueSubject<Bool, Never>(false) // Send a value to the subject, like before isLoading.send(true) // Or via the `value` setter isLoading.value = false New subscribers get a replay of the current value
Publisher provides Subscription to Subscriber receive(subscription:) Subscriber is provided to Publisher receive(subscriber:) 1 2 3 Subscription First, the Subscriber and Publisher are connected. Once they’re connected, the Publisher provides a Subscription to the Subscriber.
amongst themselves on how many values the Subscription may produce Hey there, please give me up to X values request(Subscribers.Demand) Here are some values! receive(value:) "# Subscribers.Demand Subscriber may change demand based on the value in an additive way I’m done! Here’s a completion event receive(finished:) 1 2 3 ⏳ tl;dr
key path let publisher = ["hey", "there", "Swift", “Heroes”].publisher publisher .assign(to: \.text, on: textField) Swift there Hey Heroes Publisher assign UITextField.text 1 2 3 4 There are two built-in ways to consume Publishers: sink and assign
the words. The better your vocabulary will be, the more things you can do with your data. Operators let you manipulate upstream publishers and control the emissions in extremely expressive and flexible ways Publisher Modified Publisher Operator
can be used on any kind of work you need to do map retry map retry Publisher<MyModel, Network.Error> Network Request Publisher<Data, VideoDecoding.Error> Video Decoding
constructs URLSession.shared .dataTaskPublisher(for: request) // Publisher<(Data, URLResponse), URLError> URLSession.dataTaskPublisher 9 Combine in Foundation var request = URLRequest(url: URL(string: "https://icanhazdadjoke.com/")!) request.allHTTPHeaderFields = ["Accept": "text/plain"] .sink(receiveCompletion: { finished in switch finished { case .failure(let error): print("An error occurred! \(error)") case .finished: print("Completed with no issues") } }, receiveValue: { data, response in print(String(data: data, encoding: .utf8) ?? Data()) // Can a kangaroo jump higher than the Empire State Building? // Of course. The Empire State Building can't jump. :; })
makes any variable a publisher @Published 9 Combine in Foundation $isLoading .map(!) .sink(receiveValue: { print("The opposite of loading is \($0)") }) @Published var isLoading: Bool = false isLoading // current value, accessed imperatively $isLoading // Publisher<Bool, Never> isLoading = true // Mutating notifies any subscribers
and the Reactive Streams spec, along with ones in the Swift ecosystem, for example RxSwift and ReactiveSwift. Luckily, much of your knowledge in either of them is applicable to Combine !
macOS, tvOS, watchOS, Linux iOS, macOS, tvOS, watchOS Maintained By Open Source Apple UI Bindings RxCocoa, ReactiveCocoa SwiftUI (kinda) assign (Not specific to UIKit), no publishers Error model ReactiveSwift: Typed Errors RxSwift: No Typed Errors Typed Errors Backpressure No Yes > Community = ! Combine vs. existing projects
extensions for Combine CombineCommunity/CombineDataSources Table and Collection data sources for Combine CombineCommunity/CombineExt Useful extensions Combine is missing ? sergdort/CombineFeedback Feedback-loop styled architecture based on Combine A pointfreeco/swift-composable-architecture Combine-based advanced architecture @ CombineCommunity/RxCombine Bi-directional type bridges between Combine and RxSwift
extensions for Combine CombineCommunity/CombineDataSources Table and Collection data sources for Combine CombineCommunity/CombineExt Useful extensions Combine is missing ? sergdort/CombineFeedback Feedback-loop styled architecture based on Combine A pointfreeco/swift-composable-architecture Combine-based advanced architecture @ CombineCommunity/RxCombine Bi-directional type bridges between Combine and RxSwift https://combine.community
iOS 13 and up project? Yes, Combine makes sense for your scenario! (especially if using SwiftUI) Is this an existing codebase ? Have some existing RxSwift code? Use something like RxCombine to migrate No existing Reactive code As long as you support iOS 13 and above Like anything, it depends…