HandleEvents closure not called in Swift Combine

I was addicted to using Combine because of lack of study, so I will leave it as a memorandum.

problem

In the Future type variable returned by the function

func handleEvents(
    receiveSubscription: ((Subscription) -> Void)? = nil, 
    receiveOutput: ((Output) -> Void)? = nil, 
    receiveCompletion: ((Subscribers.Completion<Failure>) -> Void)? = nil, 
    receiveCancel: (() -> Void)? = nil, 
    receiveRequest: ((Subscribers.Demand) -> Void)? = nil
) -> Publishers.HandleEvents<Future<Output, Failure>>

I want to call to do something inside the closure, but the closure doesn't run.

https://developer.apple.com/documentation/combine/future/handleevents(receivesubscription:receiveoutput:receivecompletion:receivecancel:receiverequest:)

Solutions

func sink(
    receiveCompletion: @escaping ((Subscribers.Completion<Failure>) -> Void), 
    receiveValue: @escaping ((Output) -> Void)
) -> AnyCancellable

You need to connect the subscribers by calling the above sink.

https://developer.apple.com/documentation/combine/future/sink(receivecompletion:receivevalue:)

Cause

Initially, I implemented the following code and thought that the closure set by hanleEvents would be called when promise (.success (())) was executed. But not called.

//Run on Playground
import Combine
import Foundation

struct APIError: Error {
    var description: String
}

func delay() -> Future<Void, Error> {
    return Future<Void, Error>{ promise in
        print("It will be executed")
        Thread.sleep(forTimeInterval: 2.0)
        promise(.success(()))
        return
    }
}

func executionDelay() -> AnyPublisher<Void, Error>{
    print("I will do it")
    return delay()
        .handleEvents(receiveSubscription: {_ in
            print("Subscription")
        }, receiveOutput: { _ in
            print("Output")
        }, receiveCompletion: { _ in
            print("Completion")
        }, receiveCancel: {
            print("Cancel")
        }, receiveRequest: {_ in
            print("Request")
        })
        .eraseToAnyPublisher()
}

//Execute processing
executionDelay()

If you read the handleEvents reference, it says "Performs the specified closures when publisher events occur." And closures are executed when publisher events occur. However, when I thought about it, I noticed that I was not connected to the subscriber. You need to call the sink method to connect the subscribers.

The improved code is below.

import Combine
import Foundation

func delay() -> Future<Void, Error> {
    return Future<Void, Error>{ promise in
        print("It will be executed")
        Thread.sleep(forTimeInterval: 2.0)
        promise(.success(()))
        return
    }
}

func executionDelay() -> AnyPublisher<Void, Error>{
    print("I will do it")
    return delay()
        .handleEvents(receiveSubscription: {_ in
            print("Subscription")
        }, receiveOutput: { _ in
            print("Output")
        }, receiveCompletion: { _ in
            print("Completion")
        }, receiveCancel: {
            print("Cancel")
        }, receiveRequest: {_ in
            print("Request")
        })
        .eraseToAnyPublisher()
}

//Execute processing
executionDelay()
    .sink(receiveCompletion: {completion in
        print("receiveCompletion")
        switch completion {
        case .failure(let error):
            print("Failure:\(error)")
        default:
            print("success")
        }
    }, receiveValue: { _ in
        print("receiveValue")
    })

Recommended Posts

HandleEvents closure not called in Swift Combine
[Swift] Closure
Compare objects in Swift
Combine arrays in Java
Division becomes 0 in Swift
Swift Combine First step
Multidimensional array in Swift