示例

优质
小牛编辑
137浏览
2023-12-01

计算变量

首先,让我们从一些必要的代码开始。

这个例子的目的是如果条件满足,将 ab 计算后的值绑定到 c 上。

下面就是必要的代码示例:

  1. // this is standard imperative code
  2. var c: String
  3. var a = 1 // this will only assign the value `1` to `a` once
  4. var b = 2 // this will only assign the value `2` to `b` once
  5. if a + b >= 0 {
  6. c = "\(a + b) is positive" // this will only assign the value to `c` once
  7. }

c 现在的值是 3 is positive。然而,如果我们改变了 a 的值为 4, c 依然还是老的值。

  1. a = 4 // `c` will still be equal to "3 is positive" which is not good
  2. // we want `c` to be equal to "6 is positive" since 4 + 2 = 6

这并不是想要的行为。

下面是使用 RxSwift 改进逻辑后的代码:

  1. let a /*: Observable<Int>*/ = Variable(1) // a = 1
  2. let b /*: Observable<Int>*/ = Variable(2) // b = 2
  3. // combines latest values of variables `a` and `b` using `+`
  4. let c = Observable.combineLatest(a.asObservable(), b.asObservable()) { $0 + $1 }
  5. .filter { $0 >= 0 } // if `a + b >= 0` is true, `a + b` is passed to the map operator
  6. .map { "\($0) is positive" } // maps `a + b` to "\(a + b) is positive"
  7. // Since the initial values are a = 1 and b = 2
  8. // 1 + 2 = 3 which is >= 0, so `c` is initially equal to "3 is positive"
  9. // To pull values out of the Rx `Observable` `c`, subscribe to values from `c`.
  10. // `subscribeNext` means subscribe to the next (fresh) values of `c`.
  11. // That also includes the initial value "3 is positive".
  12. c.subscribeNext { print($0) } // prints: "3 is positive"
  13. // Now, let's increase the value of `a`
  14. a.value = 4 // prints: 6 is positive
  15. // The sum of the latest values, `4` and `2`, is now `6`.
  16. // Since this is `>= 0`, the `map` operator produces "6 is positive"
  17. // and that result is "assigned" to `c`.
  18. // Since the value of `c` changed, `{ print($0) }` will get called,
  19. // and "6 is positive" will be printed.
  20. // Now, let's change the value of `b`
  21. b.value = -8 // doesn't print anything
  22. // The sum of the latest values, `4 + (-8)`, is `-4`.
  23. // Since this is not `>= 0`, `map` doesn't get executed.
  24. // This means that `c` still contains "6 is positive"
  25. // Since `c` hasn't been updated, a new "next" value hasn't been produced,
  26. // and `{ print($0) }` won't be called.

简单 UI 绑定

  • 不绑定数值,让我们用 UITextFieldrx_text 绑定数值
  • 然后,使用 mapString 转换到 Int 并且使用异步 API 判断这个数字是否是素数
  • 如果文本在异步调用完成之前改变了,一个新的异步调用会通过 concat 代替他
  • 将结果绑定到 UILabel
  1. let subscription/*: Disposable */ = primeTextField.rx_text // type is Observable<String>
  2. .map { WolframAlphaIsPrime(Int($0) ?? 0) } // type is Observable<Observable<Prime>>
  3. .concat() // type is Observable<Prime>
  4. .map { "number \($0.n) is prime? \($0.isPrime)" } // type is Observable<String>
  5. .bindTo(resultLabel.rx_text) // return Disposable that can be used to unbind everything
  6. // This will set `resultLabel.text` to "number 43 is prime? true" after
  7. // server call completes.
  8. primeTextField.text = "43"
  9. // ...
  10. // to unbind everything, just call
  11. subscription.dispose()

这个例子中使用的所有操作符是和第一个例子中使用的操作相同的。

自动完成

如果你是 Rx 的新手,那幺下面的例子在一开始可能会有一点点难以应对。但是这被用来展示 RxSwift 的代码在真实世界如何被看待。

这个例子包含复杂的异步 UI 进度通知验证逻辑。当 disposeBag 被释放时,所有操作符会被取消。

让我们赶快来看一下代码:

  1. // bind UI control values directly
  2. // use username from `usernameOutlet` as username values source
  3. self.usernameOutlet.rx_text
  4. .map { username in
  5. // synchronous validation, nothing special here
  6. if username.isEmpty {
  7. // Convenience for constructing synchronous result.
  8. // In case there is mixed synchronous and asynchronous code inside the same
  9. // method, this will construct an async result that is resolved immediately.
  10. return Observable.just((valid: false, message: "Username can't be empty."))
  11. }
  12. // ...
  13. // User interfaces should probably show some state while async operations
  14. // are executing.
  15. // Let's assume that we want to show "Checking availability" while waiting for a result.
  16. // Valid parameters can be:
  17. // * true - is valid
  18. // * false - is not valid
  19. // * nil - validation pending
  20. typealias LoadingInfo = (valid: String?, message: String?)
  21. let loadingValue : LoadingInfo = (valid: nil, message: "Checking availability ...")
  22. // This will fire a server call to check if the username already exists.
  23. // Its type is `Observable<ValidationResult>`
  24. return API.usernameAvailable(username)
  25. .map { available in
  26. if available {
  27. return (true, "Username available")
  28. }
  29. else {
  30. return (false, "Username already taken")
  31. }
  32. }
  33. // use `loadingValue` until server responds
  34. .startWith(loadingValue)
  35. }
  36. // Since we now have `Observable<Observable<ValidationResult>>`
  37. // we need to somehow return to a simple `Observable<ValidationResult>`.
  38. // We could use the `concat` operator from the second example, but we really
  39. // want to cancel pending asynchronous operations if a new username is provided.
  40. // That's what `switchLatest` does.
  41. .switchLatest()
  42. // Now we need to bind that to the user interface somehow.
  43. // Good old `subscribeNext` can do that.
  44. // That's the end of `Observable` chain.
  45. .subscribeNext { valid in
  46. errorLabel.textColor = validationColor(valid)
  47. errorLabel.text = valid.message
  48. }
  49. // This will produce a `Disposable` object that can unbind everything and cancel
  50. // pending async operations.
  51. // Instead of doing it manually, which is tedious,
  52. // let's dispose everything automagically upon view controller dealloc.
  53. .addDisposableTo(disposeBag)

这代码是最简单的。仓库中还有很多其他的例子,所以尽情查看他们吧。

这些例子包含如何使用 Rx 在 MVVM 设计模式的上下文,或者没有 MVVM。