Combine is Apple's own Functional Reactive Programming framework. In this talk, I explain three of the main pillars that Combine is built on. By re-implementing Combine's sink subscriber we explore every aspect Combine's subscription stream.
3. let publisher = URLSession.shared.dataTaskPublisher(for: someURLRequest)
4. let publisher = URLSession.shared.dataTaskPublisher(for: someURLRequest)
let sub = publisher.sink(receiveCompletion: { completionEvent in
print("The request finished with result: (completionEvent)")
}, receiveValue: { result in
print("Received result: (result)")
})
5. - Returns: A cancellable instance; used when you end assignment of the received value.
Deallocation of the result will tear down the subscription stream.
14. publisher.sink(receiveCompletion:receiveValue:)
Your code
Sink
A Subscriber is created Publisher.subscribe(_:)
Publisher
A Subscription is created subscriber.receive(subscription)
Subscriber Subscription
Receive values and request more values
Obtain values
Send values
Request values from subscription
15. publisher.sink(receiveCompletion:receiveValue:)
Your code
Sink
A Subscriber is created Publisher.subscribe(_:)
Publisher
A Subscription is created subscriber.receive(subscription)
Subscriber Subscription
Receive values and request more values
Obtain values
Send values
Send completion
Request values from subscription
16. publisher.sink(receiveCompletion:receiveValue:)
Your code
Sink
A Subscriber is created Publisher.subscribe(_:)
Publisher
A Subscription is created subscriber.receive(subscription)
Subscriber Subscription
Receive values and request more values
Obtain values
Send values
Send completionReceive completion
Request values from subscription
17. Publishers
• Create subscriptions
• Link subscriptions to subscribers
Subscriptions
• Obtain values by generating them or receiving them.
• Relay obtained values to subscribers (if demand is high enough)
32. publisher.sink(receiveCompletion:receiveValue:)
Your code
Sink
A Subscriber is created Publisher.subscribe(_:)
Publisher
A Subscription is created subscriber.receive(subscription)
Subscriber Subscription
Receive values and request more values
Obtain values
Send values
Send completionReceive completion
Request values from subscription
33. let publisher = URLSession.shared.dataTaskPublisher(for: someURLRequest)
let cancellable = publisher.dw_sink(receiveCompletion: { completion in
print("Custom chain completed: (completion)")
}, receiveValue: { result in
print("Custom chain got result: (result)")
})
34. let publisher = URLSession.shared.dataTaskPublisher(for: someURLRequest)
let cancellable = publisher.dw_sink(receiveCompletion: { completion in
print("Custom chain completed: (completion)")
}, receiveValue: { result in
print("Custom chain got result: (result)")
})
41. class DWDataTaskSubscription<S: Subscriber>: Subscription
where S.Failure == URLError,
S.Input == (data: Data, response: URLResponse) {
var subscriber: S?
let request: URLRequest
func request(_ demand: Subscribers.Demand) {
// todo: implement
}
func cancel() {
subscriber = nil
}
}
42. func request(_ demand: Subscribers.Demand) {
guard demand > 0 else { return }
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error as? URLError {
self.subscriber?.receive(completion: .failure(error))
} else if let data = data, let response = response {
self.subscriber?.receive((data, response))
self.subscriber?.receive(completion: .finished)
}
self.cancel()
// if we don't have an error AND no data, we should maybe do something.
// but we'll ignore it for now.
}.resume()
}
43. func request(_ demand: Subscribers.Demand) {
guard demand > 0 else { return }
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error as? URLError {
self.subscriber?.receive(completion: .failure(error))
} else if let data = data, let response = response {
self.subscriber?.receive((data, response))
self.subscriber?.receive(completion: .finished)
}
self.cancel()
// if we don't have an error AND no data, we should maybe do something.
// but we'll ignore it for now.
}.resume()
}
44. let publisher = URLSession.shared.dataTaskPublisher(for: someURLRequest)
let cancellable = publisher.dw_sink(receiveCompletion: { completion in
print("Custom chain completed: (completion)")
}, receiveValue: { result in
print("Custom chain got result: (result)")
})
45. let publisher = URLSession.shared.dataTaskPublisher(for: someURLRequest)
let cancellable = publisher.dw_sink(receiveCompletion: { completion in
print("Custom chain completed: (completion)")
}, receiveValue: { result in
print("Custom chain got result: (result)")
})
let publisher = DWDataTaskPublisher(request: someURLRequest)
54. publisher.sink(receiveCompletion:receiveValue:)
Your code
Sink
A Subscriber is created Publisher.subscribe(_:)
Publisher
A Subscription is created subscriber.receive(subscription)
Subscriber Subscription
Receive values and request more values
Obtain values
Send values
Request values from subscription
55. publisher.sink(receiveCompletion:receiveValue:)
Your code
Sink
A Subscriber is created Publisher.subscribe(_:)
Publisher
A Subscription is created subscriber.receive(subscription)
Subscriber Subscription
Receive values and request more values
Obtain values
Send values
Send completion
Request values from subscription
56. publisher.sink(receiveCompletion:receiveValue:)
Your code
Sink
A Subscriber is created Publisher.subscribe(_:)
Publisher
A Subscription is created subscriber.receive(subscription)
Subscriber Subscription
Receive values and request more values
Obtain values
Send values
Send completionReceive completion
Request values from subscription
57. Some tips and advise
• I mentioned it a couple of times but Apple does not recommend that you
implement custom publishers and subscriptions
• That said, it’s good to explore and see how things work.
• Retain your AnyCancellable because a in correct implementation,
Subscription will cancel as soon as the AnyCancellable is released