The Combine framework provides a declarative Swift API for processing values over time. These values can represent user interface events, network responses, scheduled events, and many other kinds of asynchronous data.
By adopting Combine, you’ll make your code easier to read and maintain, by centralizing your event-processing code and eliminating troublesome techniques like nested closures and convention-based callbacks.
public protocol Publisher {
/// The kind of values published by this publisher.
associatedtype Output
/// The kind of errors this publisher might publish.
///
/// Use `Never` if this `Publisher` does not publish errors.
associatedtype Failure : Error
/// This function is called to attach the specified `Subscriber` to
/// this `Publisher` by `subscribe(_:)`
///
/// - SeeAlso: `subscribe(_:)`
/// - Parameters:
/// - subscriber: The subscriber to attach to this `Publisher`.
/// once attached it can begin to receive values.
func receive<S>(subscriber: S) where S : Subscriber,
Self.Failure == S.Failure, Self.Output == S.Input
}
public protocol Subscriber : CustomCombineIdentifierConvertible {
/// The kind of values this subscriber receives.
associatedtype Input
/// The kind of errors this subscriber might receive.
///
/// Use `Never` if this `Subscriber` cannot receive errors.
associatedtype Failure : Error
/// Tells the subscriber that it has successfully subscribed to
/// the publisher and may request items.
///
/// Use the received `Subscription` to request items from the publisher.
/// - Parameter subscription: A subscription that represents the connection
/// between publisher and subscriber.
func receive(subscription: Subscription)
/// Tells the subscriber that the publisher has produced an element.
///
/// - Parameter input: The published element.
/// - Returns: A `Demand` instance indicating how many more elements
/// the subcriber expects to receive.
func receive(_ input: Self.Input) -> Subscribers.Demand
/// Tells the subscriber that the publisher has completed publishing,
/// either normally or with an error.
///
/// - Parameter completion: A `Completion` case indicating
/// whether publishing completed normally or with an error.
func receive(completion: Subscribers.Completion<Self.Failure>)
}
let once: Publishers.Once<Int, Never> = Publishers.Once(100)
let observer: Subscribers.Sink<Publishers.Once<Int, Never>> = Subscribers.Sink(receiveCompletion: {
print("completed: \($0)")
}, receiveValue: {
print("received value: \($0)")
})
once.subscribe(observer)
// received value: 100
// completed: finished
class Student {
let name: String
var score: Int
init(name: String, score: Int) {
self.name = name
self.score = score
}
}
let student = Student(name: "Jack", score: 90)
print(student.score)
let observer = Subscribers.Assign(object: student, keyPath: \Student.score)
let publisher = PassthroughSubject<Int, Never>()
publisher.subscribe(observer)
publisher.send(91)
print(student.score)
publisher.send(100)
print(student.score)
// 90
// 91
// 100
public protocol Subject : AnyObject, Publisher {
/// Sends a value to the subscriber.
///
/// - Parameter value: The value to send.
func send(_ value: Self.Output)
/// Sends a completion signal to the subscriber.
///
/// - Parameter completion: A `Completion` instance which indicates
/// whether publishing has finished normally or failed with an error.
func send(completion: Subscribers.Completion<Self.Failure>)
}
// Before
class ContentManager {
var content: [String] {
didSet {
delegate?.contentDidChange(content)
}
}
func getContent() {
content = ["hello", "world"]
}
}
// After
class RxContentController {
var content = CurrentValueSubject<[String], NSError>([])
func getContent() {
content.value = ["hello", "world"]
}
}
class StudentManager {
let namesPublisher: ??? // what's the type?
func updateStudentsFromLocal() {
let student1 = Student(name: "Jack", score: 75)
let student2 = Student(name: "David", score: 80)
let student3 = Student(name: "Alice", score: 96)
let namesPublisher: Publishers.Sequence<[String], Never> = Publishers.Sequence<[Student], Never>(sequence: [student1, student2, student3]).map { $0.name }
self.namesPublisher = namesPublisher
}
func updateStudentsFromNetwork() {
let namesPublisher: Publishers.Future<[String], Never> = Publishers.Future { promise in
getStudentsFromNetwork {
let names: [String] = ....
promise(.success([names]))
}
}
self.namesPublisher = namesPublisher
}
}
class StudentManager {
let namePublisher: AnyPublisher<[String, Never]>
func updateStudentsFromLocal() {
let namePublisher: AnyPublisher<[String, Never]> = Publishers.Sequence<[Student], Never>(sequence: students).map { $0.name }.eraseToAnyPublisher()
self.namePublisher = namePublisher
}
func updateStudentsFromNetwork() {
let namePublisher: AnyPublisher<[String, Never]> = Publishers.Future { promise in
promise(.success([names]))
}.eraseToAnyPublisher()
self.namePublisher = namePublisher
}
}
let justPubliser = AnyPublisher<String, NSError> { subscribe in
_ = subscribe.receive("hello") // ignore demand
subscribe.receive(completion: .finished)
}
let subscriber = AnySubscriber<String, NSError>(receiveValue: { input in
print("Received input: \(input)")
return .unlimited
}, receiveCompletion: { completion in
print("Completed with \(completion)")
})
justPubliser.subscribe(subscriber)
// Received input: hello
// Completed with finished
let apiRequest = Publishers.Future { promise in
URLSession.shared.dataTask(with: url) { data, _, _ in
promise(.success(data))
}.resume()
}
protocol Cancellable {
/// Cancel the activity.
func cancel()
}
let downloadPublisher = Future { promise in
URLSession.shared.uploadTask(with: request, fromFile: file) { (data, _, _) in
promise(.success(data))
}.resume()
}
let cancellable = downloadPublisher.sink { data in
print("Received data: \(data)")
}
// Cancel the task before it finishes
cancellable.cancel()