Maor Atlas
Maor Atlas

Reputation: 127

Make sure subscribe (sink) happens only once

I'm trying to write a "closure like" combine observers. Let's say I have MyClass

class MyClass {
    @Published var value: Int = 0

    func doSomethingClosure(completion: (Int?) -> Void) {
        value += 1
        completion(value)
    }

    func doSomethingCombine() {
        value += 1
    }
}

And I can access it using a simple function with closure

let myClass = MyClass()
myClass.doSomethingClosure { (value) in
    print(value)
}

Now, I want to do the same using a combine @Published So I will call

var cancelSet: Set<AnyCancellable> = []
myClass.$value
    .sink { (value) in
    print(value)
}.store(in: &cancelSet)

However, I want it to be triggered only once, so if I call

for _ in 0..<5 {
    myClass.doSomethingCombine()
}

I won't get 5 prints but only the first one. One solution I've found for this is cancelling the subscriber inside the sink:

myClass.$value
    .sink { (value) in
    print(value)
    **cancelSet.removeAll()**
}.store(in: &cancelSet)

but I find it kinda "not as intended".

Another solution is using the approach for back pressure, something like:

class MySubscriber: Subscriber {
    typealias Input = Int
    typealias Failure = Never
    
    func receive(subscription: Subscription) {
        subscription.request(.max(1))
    }
    
    func receive(completion: Subscribers.Completion<Never>) {
        print("Subscription \(completion)")
    }
    
    func receive(_ input: Int) -> Subscribers.Demand {
        print("Input:\(input)")
        return .none
    }
}
let mySubscriber = MySubscriber()
myClass.$value.subscribe(mySubscriber)

But I find it cumbersome and overkill for my usage.

Is there any way of implementing "closure style" in a clean way?

Thanks.

Upvotes: 2

Views: 3737

Answers (1)

Shadowrun
Shadowrun

Reputation: 3857

Sounds like a job for https://developer.apple.com/documentation/combine/publishers/prefixwhile/prefix(_:)

let numbers = (0...10)
cancellable = numbers.publisher
    .prefix(2)
    .sink { print("\($0)", terminator: " ") }

Upvotes: 5

Related Questions