2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Swift CombineにてhandleEventsのクロージャがコールされない

Last updated at Posted at 2020-12-21

Combineを使用する際に勉強不足が故にハマってしまったので、備忘録として残します。

問題

関数から返されるFuture型の変数にて

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>>

をコールしてクロージャ内で処理を行いたいが、クロージャが実行されない。

解決方法

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

上記のsinkをコールするなりして、サブスクライバーを接続する必要がある。

原因

当初は以下のコードを実装し、promise(.success(()))が実行されたタイミングでhanleEventsによって設定したクロージャがコールされると思った。
が、コールされない。

// Playgroundにて実行
import Combine
import Foundation

struct APIError: Error {
    var description: String
}

func delay() -> Future<Void, Error> {
    return Future<Void, Error>{ promise in
        print("実行されます")
        Thread.sleep(forTimeInterval: 2.0)
        promise(.success(()))
        return
    }
}

func executionDelay() -> AnyPublisher<Void, Error>{
    print("実行します")
    return delay()
        .handleEvents(receiveSubscription: {_ in
            print("Subscription")
        }, receiveOutput: { _ in
            print("Output")
        }, receiveCompletion: { _ in
            print("Completion")
        }, receiveCancel: {
            print("Cancel")
        }, receiveRequest: {_ in
            print("Request")
        })
        .eraseToAnyPublisher()
}

// 処理を実行
executionDelay()

handleEventsのリファレンスを読むと、「Performs the specified closures when publisher events occur.」とあり、パブリッシャーイベントが発生した時にクロージャが実行されるとのこと。
しかし、よくよく考えると、サブスクライバーに接続されていないことに気づいた。
sinkメソッドをコールしてサブスクライバーを接続する必要がある。

改善したコードが以下のもの。

import Combine
import Foundation

func delay() -> Future<Void, Error> {
    return Future<Void, Error>{ promise in
        print("実行されます")
        Thread.sleep(forTimeInterval: 2.0)
        promise(.success(()))
        return
    }
}

func executionDelay() -> AnyPublisher<Void, Error>{
    print("実行します")
    return delay()
        .handleEvents(receiveSubscription: {_ in
            print("Subscription")
        }, receiveOutput: { _ in
            print("Output")
        }, receiveCompletion: { _ in
            print("Completion")
        }, receiveCancel: {
            print("Cancel")
        }, receiveRequest: {_ in
            print("Request")
        })
        .eraseToAnyPublisher()
}

// 処理を実行
executionDelay()
    .sink(receiveCompletion: {completion in
        print("receiveCompletion")
        switch completion {
        case .failure(let error):
            print("失敗:\(error)")
        default:
            print("成功")
        }
    }, receiveValue: { _ in
        print("receiveValue")
    })
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?