Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Combine publishers vs completion handler and when to cancel

Tags:

swift

combine

I know in general a publisher is more powerful than a closure, however I want to ask and discuss a specific example:

func getNotificationSettingsPublisher() -> AnyPublisher<UNNotificationSettings, Never> {
   let notificationSettingsFuture = Future<UNNotificationSettings, Never> { (promise) in
      UNUserNotificationCenter.current().getNotificationSettings { (settings) in
         promise(.success(settings))
      }
   }
   return notificationSettingsFuture.eraseToAnyPublisher()
}

I think this is a valid example of a Future publisher and it could be used here instead of using a completion handler. Let's do something with it:

func test() {
    getNotificationSettingsPublisher().sink { (notificationSettings) in
       // Do something here        
    }
}

This works, however it will tell me that the result of sink (AnyCancellable) is unused. So whenever I try to get a value, I need to either store the cancellable or assign it until I get a value.

Is there something like sinkOnce or an auto destroy of cancellables? Sometimes I don't need tasks to the cancelled. I could however do this:

func test() {
   self.cancellable = getNotificationSettingsPublisher().sink { [weak self] (notificationSettings) in
      self?.cancellable?.cancel()
      self?.cancellable = nil
   }
}

So once I receive a value, I cancel the subscription. (I could do the same in the completion closure of sink I guess).

What's the correct way of doing so? Because if I use a closure, it will be called as many times as the function is called, and if it is called only once, then I don't need to cancel anything.

Would you say normal completion handlers could be replaced by Combine and if so, how would you handle receiving one value and then cancelling?

Last but not least, the completion is called, do I still need to cancel the subscription? I at least need to update the cancellable and set it to nil right? I assume storing subscriptions in a set is for long running subscriptions, but what about single value subscriptions?

Thanks

like image 943
Janosch Hübner Avatar asked May 27 '20 07:05

Janosch Hübner


People also ask

What is the use of completion handler in Swift?

A completion handler in Swift is a function that calls back when a task completes. This is why it is also called a callback function. A callback function is passed as an argument into another function. When this function completes running a task, it executes the callback function.

What is completion block in Swift?

Swift Closures with Completion handler Closures are self-contained blocks of functionality that can be passed around and used in your code. Said differently, a closure is a block of code that you can assign to a variable. You can then pass it around in your code, for instance to another function. … 7 min read.

What is PassthroughSubject?

A PassthroughSubject broadcasts elements to downstream subscribers and provides a convenient way to adapt existing imperative code to Combine. As the name suggests, this type of subject only passes through values meaning that it does not capture any state and will drop values if there aren't any subscribers set.

What is future in combine?

Essentially, a Combine Future gives us a promise closure that we can call when an asynchronous operation was completed, and Combine will then automatically map the Result that we give that closure into proper publisher events.

What is completion handler in Grok Swift?

Grok Swift. The completion handler is the code that we get to provide to get called when it comes back with those items. It’s where we can work with the results of the call: error checking, saving the data locally, updating the UI, whatever.

What is a publisher in combine?

Within the world of Combine, an object that emits such asynchronous values and events is called a publisher, and although the framework does ship with quite a large number of built-in publisher implementations, sometimes we might want to build our own, custom ones in order to handle specific situations.

What is the use of mergemany in Swift?

As you can see, the MergeMany operator allows me to create a single pipe for cached and fresh data where the cached information usually appears first and then replaced by new data. To learn about building custom Combine operators, take a look at my “Building custom Combine operators in Swift” post.

How do I add a completion handler to a closure?

To specify a completion handler we can write the closure inline like this: The code for the completion handler is the bit between the curly brackets. Notice that the three arguments in the closure (data, response, error) match the arguments in the completion handler declaration: (Data?, URLResponse?, Error?).


Video Answer


1 Answers

Instead of using the .sink operator, you can use the Sink subscriber directly. That way you don't receive an AnyCancellable that you need to save. When the publisher completes the subscription, Combine cleans everything up.

func test() {
    getNotificationSettingsPublisher()
        .subscribe(Subscribers.Sink(
            receiveCompletion: { _ in },
            receiveValue: ({
                print("value: \($0)")
            })
        ))
}
like image 196
rob mayoff Avatar answered Oct 28 '22 16:10

rob mayoff