LoginSignup
5
2

More than 3 years have passed since last update.

iOSのCoroutineについて(Async/Await)

Last updated at Posted at 2020-12-15

Abstract

現在iOSの非同期処理は一般的にGCDを使うのです、closure或いはdelegateでコールバックして、処理を追うんだが。関係性がある複数の非同期処理を実行する場合、コールバック地獄になる。

例えば、以下の画像処理のコードがある。


func fetchImage(url: String, completeHandler: @escaping (UIImage) -> ()) {
    ...
    completeHandler(image)
}

func imageDetectFace(image: UIImage, completeHandler: @escaping (UIImage) -> ()) {
    ...
    completeHandler(image)
}

func imageAddFilter(image: UIImage, completeHandler: @escaping (UIImage) -> ()) {
    ...
    completeHandler(image)
}

使う時は以下のようになる。


fetchImage(url: "hoge/url") { (originImage) in
    imageDetectFace(image: originImage) { (faceImage) in
        imageAddFilter(image: faceImage) { (filterImage) in
            imageView.image = filterImage
        }
    }
}

処理を増えると、可読性とメンテナンス性がどんどん下がる。

Introduction

上記の問題の解決策として、RxSwiftとCombineなどのライブラリを使って、リアクティブプログラミングでコールバックを撲滅するのが一番多いですが。リアクティブプログラミングのデメリットとして、debugが大変なので。
今回はcoobjcというライブラリを使って、Coroutine風に非同期処理してみる。

Coobjc

Coobjcは中国のアリババグループがOSSしたiOS用のCoroutineライブラリです。このライブラリはアセンブリとCで開発して、Objc-CとSwiftをサポートしている。このライブラリを通して、kotlin、JavaScriptやPythonなどみたいに、Coroutine風でプログラミングできる。
Github: https://github.com/alibaba/coobjc

Usage

coobjcはAsync/AwiatとGeneratorとActorなどのCoroutine機能をサポートしている。今回はAsync/Awiatだけ使ってみる。

Async/Awiat

上記のコールバック地獄のコードを、Async/Awiatを使ってCoroutine風に書き直すと、↓になる。


func fetchImage(url: String) -> Promise<UIImage> {
    return Promise(on: DispatchQueue.global()) { (fullFill, reject) in
        fullFill(UIImage())
    }
}

func imageDetectFace(image: UIImage) -> Promise<UIImage> {
    return Promise(on: DispatchQueue.global()) { (fullFill, reject) in
        fullFill(UIImage())
    }
}

func imageAddFilter(image: UIImage) -> Promise<UIImage> {
    return Promise(on: DispatchQueue.global()) { (fullFill, reject) in
        fullFill(UIImage())
    }
}

使う時は以下のようになる。

co_launch(queue: DispatchQueue.main) {
    var image: UIImage = UIImage()
    let imgResult = try await(promise: self.fetchImage(url: "url"))
    if case .fulfilled(let fetchedImg) = imgResult {
        image = fetchedImg
    } else {
        return
    }

    let faceResult = try await(promise: self.imageDetectFace(image: image))
    if case .fulfilled(let faceImg) = faceResult {
        image = faceImg
    } else {
        return
    }

    let filterResult = try await(promise: self.imageAddFilter(image: image))
    if case .fulfilled(let filterImg) = filterResult {
        imageView.image = filterImg
    }
}

これで非同期処理が終わったらMainThreadに戻り、imageViewを更新する。GCDとコールバックもすっきりになりました。

Conclusion

もしiOSで、Coroutine風にプログラミングしたい場合は、Coobjcが結構役に立てると思いますけど、プロジェクトへの侵入性がちょっと強くて、やめたい時は面倒になる。

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