LoginSignup
19
11

More than 5 years have passed since last update.

[翻訳] なぜRxSwiftを使うのか? (RxSwift-Chinese-Documentation)

Last updated at Posted at 2017-12-19

中国語で書かれたRxSwiftのドキュメントが非常に参考になりました。
他の方のRxSwiftの勉強に少しでも役に立てばと思い、日本語に翻訳してみました!
今回は1章の「なぜRxSwiftを使うのか?」となります。

原文: https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/why_rxswift.html

Target Action

従来のやり方

button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
func buttonTapped() {
    print("button Tapped")
}

Rxでやるなら

button.rx.tap
    .subscribe(onNext: {
        print("button Tapped")
    })
    .disposed(by: disposeBag)

Target Actionではなくこちらを使えばコードのロジックがはっきりと見えるようになります。

Delegate

従来のやり方

class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
    }
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("contentOffset: \(scrollView.contentOffset)")
    }
}

Rxでやるなら

class ViewController: UIViewController {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()

        scrollView.rx.contentOffset
            .subscribe(onNext: { contentOffset in
                print("contentOffset: \(contentOffset)")
            })
            .disposed(by: disposeBag)
    }
}

これで望む結果が取れるので、Delegateを書く必要はありません。

Closure コールバック

従来のやり方

URLSession.shared.dataTask(with: URLRequest(url: url)) {
    (data, response, error) in
    guard error == nil else {
        print("Data Task Error: \(error!)")
        return
    }

    guard let data = data else {
        print("Data Task Error: unknown")
        return
    }

    print("Data Task Success with count: \(data.count)")
}.resume()

Rxでやるなら

URLSession.shared.rx.data(request: URLRequest(url: url))
    .subscribe(onNext: { data in
        print("Data Task Success with count: \(data.count)")
    }, onError: { error in
        print("Data Task Error: \(error)")
    })
    .disposed(by: disposeBag)

コールバックは非常にシンプルになります。

Notification

従来のやり方

var ntfObserver: NSObjectProtocol!

override func viewDidLoad() {
    super.viewDidLoad()

    ntfObserver = NotificationCenter.default.addObserver(
          forName: .UIApplicationWillEnterForeground,
          object: nil, queue: nil) { (notification) in
        print("Application Will Enter Foreground")
    }
}

deinit {
    NotificationCenter.default.removeObserver(ntfObserver)
}

Rxでやるなら

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.rx
        .notification(.UIApplicationWillEnterForeground)
        .subscribe(onNext: { (notification) in
            print("Application Will Enter Foreground")
        })
        .disposed(by: disposeBag)
}

Observerのライフサイクルを管理する必要がないため、ロジックに集中することができます。

KVO

従来のやり方

private var observerContext = 0

override func viewDidLoad() {
    super.viewDidLoad()
    user.addObserver(self, forKeyPath: #keyPath(User.name), options: [.new, .initial], context: &observerContext)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if context == &observerContext {
        let newValue = change?[.newKey] as? String
        print("do something with newValue")
    } else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
    }
}

deinit {
    user.removeObserver(self, forKeyPath: #keyPath(User.name))
}

Rxでやるなら

override func viewDidLoad() {
    super.viewDidLoad()

    user.rx.observe(String.self, #keyPath(User.name))
        .subscribe(onNext: { newValue in
            print("do something with newValue")
        })
        .disposed(by: disposeBag)
}

これにより、KVOのコードはより明確かつキレイになります。

複数の処理間に依存関係がある場合

例えば、最初にパスワードとユーザー名からトークンを取得し、トークンを介してユーザ情報を取得するケースです。

従来のやり方

/// コールバック・パッケージ・インタフェース
enum Api {

    /// ユーザ名とパスワードでトークンを取得する
    static func token(username: String, password: String,
        success: (String) -> Void,
        failure: (Error) -> Void) { ... }

    /// トークンによるユーザー情報の取得
    static func userinfo(token: String,
        success: (UserInfo) -> Void,
        failure: (Error) -> Void) { ... }
}

/// ユーザー名とパスワードでユーザー情報を取得する
Api.token(username: "beeth0ven", password: "987654321",
    success: { token in
        Api.userInfo(token: token,
            success: { userInfo in
                print("ユーザ情報取得成功: \(userInfo)")
            },
            failure: { error in
                print("ユーザ情報取得失敗: \(error)")
        })
    },
    failure: { error in
        print("ユーザ情報取得失敗: \(error)")
})

Rxでやるなら

/// Rxを使用したインターフェース
enum Api {

    /// ユーザ名とパスワードでトークンを取得する
    static func token(username: String, password: String) -> Observable<String> { ... }

    /// トークンによるユーザー情報の取得
    static func userInfo(token: String) -> Observable<UserInfo> { ... }
}

/// ユーザー名とパスワードでユーザー情報を取得する
Api.token(username: "beeth0ven", password: "987654321")
    .flatMapLatest(Api.userInfo)
    .subscribe(onNext: { userInfo in
        print("ユーザ情報取得成功: \(userInfo)")
    }, onError: { error in
        print("ユーザ情報取得失敗: \(error)")
    })
    .disposed(by: disposeBag)

コードのネストを少なくすることができ、コードの可読性があがります。

複数の並列処理の完了を待つ場合

例えば、2つの通信結果をマージするケースです。

Rxでやるなら

/// Rx用インターフェース
enum Api {

    /// 先生の情報を取得する
    static func teacher(teacherId: Int) -> Observable<Teacher> { ... }

    /// 先生のコメントを取得する
    static func teacherComments(teacherId: Int) -> Observable<[Comment]> { ... }
}

/// 先生の情報とコメントを同時に取得する
Observable.zip(
      Api.teacher(teacherId: teacherId),
      Api.teacherComments(teacherId: teacherId)
    ).subscribe(onNext: { (teacher, comments) in
        print("先生の情報取得成功: \(teacher)")
        print("先生のコメント取得成功: \(comments.count) 件")
    }, onError: { error in
        print("先生の情報とコメント取得失敗: \(error)")
    })
    .disposed(by: disposeBag)

なぜRxSwiftを使うのか?

  • 組み合わせ - Rxは組み合わせと同義である
  • 再利用 - 組み合わせることは容易であるため
  • クリア - 宣言は不変であるため
  • 使いやすさ - 抽象的な非同期プログラミングのために、コードスタイルを統一できる
  • 安定性 - Rxは全てユニットテストされているため

(翻訳が間違ってたら教えてくださいm(_ _)m)

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