📝

RxSwiftをasync/awaitに変換する

2023/06/05に公開

RxSwiftをasync/awaitに変換する

はじめに

RxSwiftを使っている中で、async/awaitに変換して使いたい時があります。

具体的な場面としては、refreshable View Modifierでasync/awaitを使いたい時があります。

func refreshable(action: @escaping () async -> Void) -> some View

refreshable View Modifierでは、非同期関数を実行することができ、インジケータと同期されています。

成功時

エラー時

(非同期関数の実行結果を受け取ってからインジケータが非表示になります)

参考: refreshable(action: )

refreshable View Modifierで同期関数を実行すると、レスポンス待ちなのですが、インジケータが一回表示されて、インジケータは非表示になります。

実装方法

RefreshableSampleView.swift

protocol RefreshableSampleViewProtocol: AnyObject {
  func refresh() async
}

struct RefreshableSampleView: View {

  // ...

  var body: some View {
    switch dataSource.viewState {
      case .success:
        successView()
      case .failed:
        failedView()
    }
  }

  var successView: some View {
    ScrollView {
      Text("Hello World!")
    }
    .refreshable {
      await delegate?.refresh()
    }
  }

  var failedView: some View {
    Text("Error")
  }
}

Viewのコードでは、delegateメソッドを利用し、VCにロジックを寄せています。

refreshable View Modifier内で非同期関数を呼んでいます。

RefreshableSampleViewController.swift

final class RefreshableSampleViewController: UIViewController {
  // ...
}

extension RefreshableSampleViewController: RefreshableSampleViewProtocol {
  func refresh() async {

    // pull to refreshで実行されるAPIを叩く
    viewModel.inputs.refreshTrigger.accept(())

    // didRefresh: Driver<Void>を受け取る
    for await value in viewModel.outputs.didRefresh.values { return }

  }
}

ViewControllerのコードでは、Delegateプロトコルに準拠し、refresh非同期関数を実装しています。

最初にpull to refreshで実行されるAPIを叩きます。

次に、Driver<Void>型のプロパティをawaitを用いて値を取り出せるように待ちます。

値を取得できると、Viewのインジケータも閉じられるようになります。

(APIから取得した値は、省略していますが別途ViewModelから受け取るようにしています)

まとめ

RxSwiftのDriverrefreshable View Modifier内でasync/awaitに変換することができるようになりました。

RxSwiftのDriverSingleでは、例外処理が発生しないので、(エラーをスローしないので)VCやViewのロジックで例外処理を実装する必要がなさそうです。

参考: Awaiting a non-throwing sequence

Discussion