post Image
Actionを使って快適なViewModel生活を🏄

Actionを使って快適なViewModel生活を🏄

アプリを開発してる上でAPI通信は欠かせませんが、RxSwiftを使って開発している場合
API通信で起こるエラーを正しく処理しなくてはなりません!

Observableの代わりにDriverを使うのも一手ですが、オペレーターが充実していないので困ることもあるかと思います。

今回はActionというライブラリーを使ってAPI通信・エラーの処理を行う方法を書きます。

RxSwiftで実装した場合

例えばViewModelでAPIからFooの配列を取得する場合、まずは以下のように書くことができる。

class FooViewModel {

    let refreshTrigger = PublishSubject<Void>()
    let foos = PublishSubject<[Foo]>()

    init() {
        refreshTrigger
            .flatMap { APIClient.shared.responseFrom(request) }
            .bindTo(foos)
            .addDisposeBag(disposeBag)
    }
}

しかしこのコードだと以下のような問題があります

  • Responseがエラーだった場合にfoos.errorが流れてしまいUIのバインディングが切れてしまう。
  • foosのプロパティーがViewController側からアクセスできてしまう。(ReadOnlyにするべき)

上記の問題を解決するには以下のように変更することができます。

class FooViewModel {

    let refreshTrigger = PublishSubject<Void>()

    var foos: Observable<[Foo]> {
        return foosSubject
    }

    var error: Observable<Error> {
        return errorSubject
    }

    private let foosSubject = PublishSubject<[Foo]>()
    private let errorSubject = PublishSubject<Error>()

    init() {
        refreshTrigger
            .flatMap { [weak self] _ in 
                return APIClient.shared.responseFrom(request)
                    .catchError { _ in .empty } // catchErrorJustReturn([])も可能
                    .do(onError: { [weak self] error in 
                        self?.errorSubject.onNext(error)
                    })
            }
            .bindTo(foosSubject)
            .addDisposeBag(disposeBag)
    }
}
  • Responseが.errorの場合に.emptyを返してストリームを維持する
  • ViewModelからのアウトプットをComputed Propertyにして外部から変更できないようにする。
  • エラーをViewControllerで処理できるようにする。

RxSwiftを使って開発している場合、ViewModelは大体こんな感じになっていると思う!

Actionを導入する🏄

Actionってなに??

ReadMeにはこのように書かれています

An action is a way to say “hey, later I’ll need you to subscribe to this thing.” It’s actually a lot more >involved than that.

Actions accept a workFactory: a closure that takes some input and produces an observable. When execute() is called, it passes its parameter to this closure and subscribes to the work.

  • Can only be executed while “enabled” (true if unspecified).
  • Only execute one thing at a time.
  • Aggregates next/error events across individual executions.

まとめるとこんな感じ

  • トリガーとなるInputを受け取り、workFactoryというクロージャーを実行し、OutputObservableで吐き出す。
  • 一度に実行できるのは1個だけ(直列処理)
  • .next.errorを別々に扱う

Actionの実装について

Actionの中には以下のようなプロパティーが用意されている。

public final class Action<Input, Element> {
    public typealias WorkFactory = (Input) -> Observable<Element>
    public let workFactory: WorkFactory

    public let inputs = PublishSubject<Input>()
    public let errors: Observable<ActionError>
    public let elements: Observable<Element>
}

実行される順序

  1. inputsonNextするのをトリガーにworkFactoryが実行される
  2. workFactoryの結果がelementsに流れてくる(これがアウトプット)
  3. もしworkFactoryの中で.errorが起きたらerrorsに流れる。

つまりどういうことができるのか!👀

Actionを使って上のViewModelを書き換えると以下のように書くことができます!

class FooViewModel {

    let refreshTrigger = PublishSubject<Void>()

    var foos: Observable<[Foo]> {
        return refreshAction.elements
    }

    var error: Observable<Error> {
        return refreshAction.errors
            .flatMap { actionError -> Observable<Error> in
                if case .underlayingError(let error) = actionError {
                    return Observable.of(error)
                } else {
                    return .empty()
                }
            }
    }

    private let refreshAction = Action<Void, [Foo]> { _ in
        return APIClient.shared.responseFrom(request)
    }

    init() {
        refreshTrigger
            .bindTo(refreshAction.inputs)
            .addDisposeBag(disposeBag)
    }
}
  • refreshActionInputVoidなのでrefreshTriggeractioninputsにバインドできる。
  • refreshTriggeronNextされたタイミングでaction内でAPI通信が行われる。
  • Responseが指定した型(この場合は[Foo])でelementsに流れてくるので、そのままComputed Propertyの値に使う。
  • Errorの場合はactionのerrorsに流れてくるので、これもそのままComputed Propertyの値に使う。

まとめ

Actionを使うことによって

  • APIの処理を切り出して行うことができる。
  • Errorが流れてしまってUIのバインディングが切れることはない。
  • ViewModelからのアウトプットをComputed Propertyで定義する時にPublishSubjectなどを経由しなくて良い。

是非一度レポジトリーをのぞいてみてください🚀


『 Swift 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。