Swift Combine: Waiting until subscribed to generate values - swift

I'm trying to write a custom publisher that generates some values. Something like this:
class MyPublisher: Publisher {
typealias Output = Int
typealias Failure = Never
private let subject = PassThroughSubject<Int, Never>()
func receive<S>(subscriber: S) where S: Subscriber, S.Failure == Failure, S.Input == Output {
subject.receive(subscriber: subscriber)
startSending()
}
func startSending() {
subject.send(1)
subject.send(2)
subject.send(3)
subject.send(completion: .finished)
}
}
I'm trying to figure out how to call startSending() automatically after a subscribing attaches, but I'm not sure if I'm doing it right.
I've just been reading about ConnectablePublisher and was wondering if that might help, but I'm not sure how.
Has anyone tried something like this? How did you do it?

As a continuation of this. One of the changes I've been experimenting with is to locally declared the subject instead of having it as a class variable. I think that solves the issue some raised about multiple subscribers.
public func receive<S>(subscriber: S) where S: Subscriber, S.Failure == Failure, S.Input == Output {
let subject = PassthroughSubject<Int, Error>()
defaultValueSubject.receive(subscriber: subscriber)
startSending(to: subject)
}
func startSending(to subject: PassThroughSubject<Int>) {
subject.send(1)
subject.send(2)
subject.send(3)
subject.send(completion: .finished)
}

Related

Making custom Deffered Future Publisher in Swift Combine?

Like the title says I would like to make custom publisher that will basically function like deffered future. Normally when I want to encapsulate code in some Future, but want it to execute on subscription, I would need to write something like this:
Deffered {
Future { promise in
}
}
Now I was thinking of making custom publisher, something along the lines DefferedFuture that will have exact same functionality as Future, but will execute promise only on subscription?
The most obvious answer is this:
func single<Output, Failure>(_ promise: #escaping (#escaping (Result<Output, Failure>) -> Void) -> Void) -> Deferred<Future<Output, Failure>> where Failure: Error {
Deferred {
Future<Output, Failure>(promise)
}
}
If it absolutely must be a type rather than a function then:
extension Publishers {
struct Single<Output, Failure>: Publisher where Failure: Error {
let promise: (#escaping (Result<Output, Failure>) -> Void) -> Void
func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input {
Deferred { Future(promise) }
.subscribe(subscriber)
}
}
}

Read only CurrentValueSubject?

Is there a way to create a CurrentValueSubject that is read-only?
So you could sink it publicly, read value publicly, but could only send values to it internally/privately. Want to use it in a library module.
The best pattern is to have it declared private:
private let _status = CurrentValueSubject<ThisStatus?, Never>(nil)
and expose it through a computed property:
public var status: AnyPublisher<ThisStatus?, Never> {
_status
.eraseToAnyPublisher()
}
I ended up creating a Publisher wrapping a CurrentValueSubject.
This way it can be written internally (to the module), but other modules can only read/subscribe to the wrapping publisher.
public class ReadOnlyCurrentValueSubject<Output, Failure>: Publisher where Failure : Error {
internal let currentValueSubject: CurrentValueSubject<Output, Failure>
public internal(set) var value: Output {
get { currentValueSubject.value }
set { currentValueSubject.value = newValue }
}
public init(_ value: Output) {
currentValueSubject = .init(value)
}
public func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
currentValueSubject.receive(subscriber: subscriber)
}
}
With having internal access to the currentValueSubject the module can freely compose it internally, while the outside world can only consume the values.
You can write a custom publisher that wraps a CurrentValueSubject, and that exposes the subject only at initialization time. This way, the code that creates the publisher is the only one having access to the subject, and is able to instruct the publisher to emit events.
The new publisher can look like this:
extension Publishers {
public struct CurrentValue<Value, Failure: Error>: Publisher {
public typealias Output = Value
public typealias Subject = CurrentValueSubject<Value, Failure>
public var value: Value { subject.value }
private var subject: Subject
public static func publisher(_ initialValue: Value) -> (Self, Subject) {
let publisher = Self(initialValue)
return (publisher, publisher.subject)
}
private init(_ initialValue: Value) {
subject = Subject(initialValue)
}
public func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Value == S.Input {
subject.receive(subscriber: subscriber)
}
}
}
, and can be consumed in this fashion:
class MyClass {
// use this to expose the published values in a readonly manner
public let publisher: Publishers.CurrentValue<Int, Never>
// use this to emit values/completion
private var subject: Publishers.CurrentValue<Int, Never>.Subject
init() {
(publisher, subject) = Publishers.CurrentValue.publisher(10)
}
}
This way you have a readonly value publisher, and the only instance that can publish values is the one that instantiates the publisher.
Now, if the internal requirement you specified in the question must be taken ad-literam, then you can change the visibility of the CurrentValue.subject property to be internal, in this case you no longer need the static method.
A good replacement in this case is:
#Published public private(set) var status: ThisStatus? = nil
Use $status to get the underlying publisher.

Why .collect() operator in swift Combine always sends .unlimited demand regardless of the demand of upstream publisher?

I have been playing with Combine to understand how it works in more details and I create a custom Publisher, Subscription and Subscriber.
Here's how it looks..
The emoji beamer publisher along with subscription:
struct EmojiBeamerPublisher: Publisher {
typealias Output = String
typealias Failure = Error
private let emojis: [String] = ["πŸ‘","❀️","βœ…","πŸ₯°","😍","πŸš€","πŸ˜…","πŸ‘","🍞","πŸŽ…","❄️","🐻","πŸ‘€","πŸ‘„","🦷","✍️","πŸ™","πŸ‘¨β€πŸ’»","🐝","πŸ›","πŸ¦‰","πŸ¦€","🐍","🐞","🧸"]
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
let subscription = EmojiBeamerSubscription(output: emojis, subscriber: subscriber)
subscriber.receive(subscription: subscription)
}
}
extension EmojiBeamerPublisher {
private final class EmojiBeamerSubscription<S: Subscriber>: Subscription where S.Input == Output, S.Failure == Failure {
var subscriber: S?
let output: [String]
init(output: [String], subscriber: S) {
self.subscriber = subscriber
self.output = output
}
func request(_ demand: Subscribers.Demand) {
Swift.print("Demand: \(demand)") // Here I receive Unlimited demand
var demand = demand
Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { [weak self] timer in
guard let self = self else { return }
guard demand > 0, let subscriber = self.subscriber else {
timer.invalidate()
self.subscriber?.receive(completion: .finished)
self.cancel()
return
}
demand -= 1
demand += subscriber.receive(self.output.randomElement()! + " \(Date())")
}
}
func cancel() {
subscriber = nil
}
}
}
Here is my Custom subscriber:
final class EmojiBeamerSubscriber<Input, Failure: Error>: Subscriber, Cancellable {
var subscription: Subscription?
let receiveValue: (Input) -> Void
init(receiveValue: #escaping (Input) -> Void) {
self.receiveValue = receiveValue
}
func receive(subscription: Subscription) {
self.subscription = subscription
subscription.request(.max(3)) // Here I send only 3 as max demand
}
func receive(_ input: Input) -> Subscribers.Demand {
receiveValue(input)
return .none
}
func receive(completion: Subscribers.Completion<Failure>) {
print("Will handle later:", completion)
}
func cancel() {
self.subscription?.cancel()
self.subscription = nil
}
}
extension Publisher {
func myCustomSink(receiveValueHandler: #escaping (Self.Output) -> Void) -> AnyCancellable {
let myCustomSubscriber = EmojiBeamerSubscriber<Self.Output, Self.Failure>(receiveValue: receiveValueHandler)
subscribe(myCustomSubscriber)
return AnyCancellable(myCustomSubscriber)
}
}
As you can see on my custom subscription I request with demand .max(3) if I don't use collect everything works fine, I get an emoji beamed every 5 second after 3 I got a .finish completion.
Works fine (and sends .max(3) demand):
let emojiBeamer = EmojiBeamerPublisher()
var cancellables = Set<AnyCancellable>()
emojiBeamer
.myCustomSink { value in Swift.print("Random Emoji:: \(value)") }
.store(in: &cancellables)
However if I simply add .collect() to catch all 3 results at once in an array it just requests with .unlimited demand on my subscription, resulting in a never ending subscription because my demand will never reach zero.
Never complete (and sends unlimited demand):
let emojiBeamer = EmojiBeamerPublisher()
var cancellables = Set<AnyCancellable>()
emojiBeamer
.collect()
.myCustomSink { value in Swift.print("Random Emoji:: \(value)") }
.store(in: &cancellables)
Is there something wrong with my implementation? Or Did I misunderstood the purpose of .collect() operator?
Thank you in advance :)
From the documentation:
This publisher requests an unlimited number of elements from the upstream publisher and uses an unbounded amount of memory to store the received values. The publisher may exert memory pressure on the system for very large sets of elements.
So the behaviour you noticed is the correct one, as collect() sends an unlimited demand upstream.
The unlimited demand causes the demand -= 1 instruction to do nothing, so the demand > 0 check will always pass, resulting into an infinite loop that never sends the completion. You will need an extra condition to make the "collected" stream a finite one.
For infinite streams, the collect(_:) overload (the one that allows to pass a number of items to collect) performs better in regards to the demand, but still requests from upstream more elements than one might expect:
When this publisher receives a request for .max(n) elements, it requests .max(count * n) from the upstream publisher.

How do I forward Publisher output to a downstream Subscriber through a custom operator?

I have a potential use for Combine, but I am having a lot of trouble with the implementation details. The goal is to provide an Publisher that will do the following:
Search for a cached value, and emit that value, or:
Refer the subscriber to an upstream publisher that will emit a value, storing it in the appropriate cache location
I understand that this could be done using existing operators, but I would like to learn how to make a custom Operator/Publisher/Subscription pattern, if possible.
I'd like the usage to be similar to the following bit of pseduocode:
URLSession.shared.dataTaskPublisher(for: url)
.cache(with: { someSortOfCachingPolicy })
.sink()
In order to implement this, I am guessing at what Apple does for things like map and flatMap.
I have created a CachePublisher to try to capture the Upstream Publisher:
struct CachePublisher<Upstream: Publisher>: Publisher {
typealias Output = Upstream.Output
typealias Failure = Upstream.Failure
var upstream: Upstream
var getCache: ()->Output?
var setCache: (Output)->Void
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
let subscription = CachePublisherSubscription(subscriber: subscriber, upstream: upstream, getCache: getCache, setCache: setCache)
subscriber.receive(subscription: subscription)
}
init(_ upstream: Upstream, getCache: #escaping ()->Output?, setCache: #escaping (Output)->Void) {
self.upstream = upstream
self.getCache = getCache
self.setCache = setCache
}
}
This is followed up with a Subscription:
extension CachePublisher {
class CachePublisherSubscription<S: Subscriber>: Subscription where S.Input == Upstream.Output, S.Failure == Upstream.Failure {
var subscriber: S
var upstream: Upstream
var setCache: (Output)->Void
var getCache: ()->Output?
init(subscriber: S, upstream: Upstream, getCache: #escaping ()->Output?, setCache: #escaping (Output)->Void) {
self.subscriber = subscriber
self.upstream = upstream
self.getCache = getCache
self.setCache = setCache
}
func request(_ demand: Subscribers.Demand) {
///check the cache for a value that satisfies the type
///return a value from the upstream publisher if not
if let output = self.getCache() {
subscriber.receive(output)
} else {
//forward an upstream value?
//how? an entire publisher/subscriber chain?
}
}
func cancel() {
}
}
}
And finally, a function so you can pass the upstream publisher to the CachePublisher
extension Publisher {
func cache() -> CachePublisher<Self> {
return CachePublisher(self, getCache: { nil }, setCache: { _ in })
}
}
I have no idea what to put in the required methods, or how to pass the subscriber up the chain to the upstream publisher. Or how to capture values from the upstream publisher.
The idea that came into my head is that downstream subscribers sort of create a nesting doll type structure, but I just don't know how to connect them.
You don't need the whole Publisher/Publishers/Subscription dance, you can customize the subscribe method without needing a custom class. Existing Combine operators to the rescue here :).
extension Publisher {
func cache(read: #escaping Publishers.Cache<Self>.Read,
write: #escaping Publishers.Cache<Self>.Write) -> Publishers.Cache<Self> {
Publishers.Cache(upstream: self, read: read, write: write)
}
}
extension Publishers {
struct Cache<P: Publisher>: Publisher {
typealias Output = P.Output
typealias Failure = P.Failure
typealias Read = () -> Output?
typealias Write = (Output) -> Void
private let upstream: P
private let read: Read
private let write: Write
init(upstream: P, read: #escaping Read, write: #escaping Write) {
self.upstream = upstream
self.read = read
self.write = write
}
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input {
if let cachedValue = read() {
Just(cachedValue).setFailureType(to: Failure.self).receive(subscriber: subscriber)
} else {
upstream.handleEvents(receiveOutput: write).receive(subscriber: subscriber)
}
}
}
}
handleEvents kinda breaks the "pure functions" paradigm that is recommended to be followed when writing custom operators pipelines, however as you anyway need to write to the cache, and that's already a side effect, the added impact of calling handleEvents is not that big.
Making the custom Subscription also a Subscriber allows it to connect both directions. When the cache getter produces a result, it is sent to the downstream subscriber. However, when the getter does not, the demand is forwarded to the upstream publisher, which emits a value.
That value is then captured by the Subscriber methods of the custom Subscription and forwarded to the downstream subscriber.
extension CachePublisher {
class CachePublisherSubscription<Downstream: Subscriber>: Subscription, Subscriber where Downstream.Input == Upstream.Output, Downstream.Failure == Upstream.Failure {
typealias Input = Upstream.Output
typealias Failure = Upstream.Failure
var downstream: Downstream
var upstream: Upstream
var upstreamSubscription: Subscription?
var read: Read
var write: Write
init(downstream: Downstream, upstream: Upstream, read: #escaping Read, write: #escaping Write) {
self.downstream = downstream
self.upstream = upstream
self.read = read
self.write = write
upstream.subscribe(self)
}
func request(_ demand: Subscribers.Demand) {
if let cachedValue = read() {
downstream.receive(cachedValue)
} else {
upstreamSubscription?.request(demand)
}
}
// keep a reference to the upstream subscription
func receive(subscription: Subscription) {
self.upstreamSubscription = subscription
}
// pass input downstream
func receive(_ input: Input) -> Subscribers.Demand {
self.write(input)
return downstream.receive(input)
}
// pass completion downstream
func receive(completion: Subscribers.Completion<Failure>) {
downstream.receive(completion: completion)
}
func cancel() {
//TO-DO: Finish cancellation
}
}
}

Generic messages in message-based architecture

I am experimenting with message-based architecture in Swift. I am trying to do something similar to the Elm Architecture, for example. This is how my code looks:
enum SideEffect<Message> {
case sendRequest((String) -> Message)
}
protocol Component {
associatedtype Message
mutating func send(msg: Message) -> [SideEffect<Message>]
}
struct State: Component {
var something: String?
enum Message {
case downloadSomething
case receiveResponse(String)
}
mutating func send(msg: Message) -> [SideEffect<Message>] {
switch msg {
case .downloadSomething:
return [.sendRequest(Message.receiveResponse)]
case .receiveResponse(let response):
something = response
return []
}
}
}
So the state is modelled by State and you can change it by sending Messages. If there are any side effects to compute, they are returned as a SideEffect message and will be taken care of by someone else. Each SideEffect message takes a β€œcallback” argument, a Message to send when the side effect is finished. This works great.
Now, what if I want to have a generic side effect message? I would like to have something like this:
struct Request<ReturnType> { … }
And have a related side effect to load the request and return a value of type ReturnType:
enum SideEffect<Message> {
case sendRequest(Request<T>, (T) -> Message)
}
But this (obviously) doesn’t compile, as the case would have to be generic over T. I can’t make the whole SideEffect generic over T, since there’s other side effects that have nothing to do with T.
Can I somehow create a SideEffect message with a Request<T> that would later dispatch a Message with T? (I think I want something like this feature discussed on swift-evolution.)
You'll want to type erase T – usually this can be done with closures, as they can reference context from the site at which they're created, without exposing that context to the outside world.
For example, with a mock Request<T> (assuming it's an async operation):
struct Request<T> {
var mock: T
func doRequest(_ completion: #escaping (T) -> Void) {
// ...
completion(mock)
}
}
We can build a RequestSideEffect<Message> that holds a closure that takes a given (Message) -> Void callback, and then performs a request on a captured Request<T> instance, forwarding the result through a (T) -> Message, the result of which can then be passed back to the callback (thus keeping the type variable T 'contained' in the closure):
struct RequestSideEffect<Message> {
private let _doRequest: (#escaping (Message) -> Void) -> Void
init<T>(request: Request<T>, nextMessage: #escaping (T) -> Message) {
self._doRequest = { callback in
request.doRequest {
callback(nextMessage($0))
}
}
}
func doRequest(_ completion: #escaping (Message) -> Void) {
_doRequest(completion)
}
}
Now your SideEffect<Message> can look like this:
enum SideEffect<Message> {
case sendRequest(RequestSideEffect<Message>)
}
And you can implement State like this:
protocol Component {
associatedtype Message
mutating func send(msg: Message) -> [SideEffect<Message>]
}
struct State: Component {
var something: String
enum Message {
case downloadSomething
case receiveResponse(String)
}
mutating func send(msg: Message) -> [SideEffect<Message>] {
switch msg {
case .downloadSomething:
let sideEffect = RequestSideEffect(
request: Request(mock: "foo"), nextMessage: Message.receiveResponse
)
return [.sendRequest(sideEffect)]
case .receiveResponse(let response):
something = response
return []
}
}
}
var s = State(something: "hello")
let sideEffects = s.send(msg: .downloadSomething)
for case .sendRequest(let sideEffect) in sideEffects {
sideEffect.doRequest {
_ = s.send(msg: $0) // no side effects expected
print(s) // State(something: "foo")
}
}