RxSwift I:开始学习 RxSwift

栏目: Swift · 发布时间: 6年前

内容简介:RxSwift 是一个组合异步和事件驱动编程的库,通过使用可观察序列和功能样式运算符来,从而允许通过调度程序进行参数化执行。RxSwift 在本质上简化了开发异步程序,允许代码对新数据作出反应,并以顺序和孤立的方式处理它。

RxSwift I:开始学习 RxSwift

RxSwift 是一个组合异步和事件驱动编程的库,通过使用可观察序列和功能样式运算符来,从而允许通过调度程序进行参数化执行。

RxSwift 在本质上简化了开发异步程序,允许代码对新数据作出反应,并以顺序和孤立的方式处理它。

介绍异步编程

RxSwift I:开始学习 RxSwift

使用 Cocoa 和 UIKit 异步的 API 的问题在于:复杂的异步代码变得非常难写,部分原因是苹果 SDK 提供的 API 种类繁多。

异步编程词汇表:

  1. 状态(State),具体地说,共享可变状态
  2. 命令式程序设计
  3. 副作用
  4. 宣告式編程:允许你定义行为片断, RxSwift 会在有相关事件的时候运行这些行为,并为它们提供一个不可变的,孤立的数据输入。
  5. 响应式系统(Reactive systems)

反应式系统是一个相当抽象的术语,它涵盖了 Web 或 iOS 应用程序,它们显示了大多数或全部以下特性:

  • 反应(Responsive):始终保持 UI 更新,代表了最新的应用程序状态。
  • 能復原的(Resilient):每个行为被隔离开来,并提供可恢复的错误恢复。
  • 灵活的(Elastic): 该代码处理不同的工作负载,通常实现诸如懒惰驱动数据收集、事件节流和资源共享等特性。
  • 消息驱动(Message driven):组件使用基于消息的通信来提高可重用性和隔离性,解耦生命周期和类的实现。

RxSwift 基础

RxSwift I:开始学习 RxSwift

这个标志是一只电鳗。(Rx 项目曾经被称为 Volta)

RxSwift 是微软开源的 ReactiveX 的 Swift 语言的实现。

RxSwift在传统的 Cocoa 编程和纯函数编程之间找到了最佳位置。它允许您对事件作出反应, 方法是使用不可变的代码定义以确定性的、可组合的方式处理异步输入部分。

Rx 代码的三个组成部分是 observables, operators 和 schedulers

Observable 类提供了 Rx 代码的基础:异步产生一系列事件的能力,它可以“携带”数据的不可变快照。简单来说,它允许类在一段时间内订阅其他类发出的值。

ObservableType 协议 (Observable 需要符合的) 非常简单。可观测的可能发出 (并且观察员能接受) 仅三类型事件:

- next 下一个事件: “携带” 最新 (或 “下一个 “) 数据值的事件。这是观察者 “接收” 值的方式。

- completed 已完成的事件: 此事件以成功终止事件序列。这意味着可观察的完成其生命周期成功, 不会发出任何其他事件。

- error 错误事件: 可观察的终止带有错误, 不会发出其他事件.

两种不同的可观测序列: finite 和 infinite。

由于它们是高度解耦和可组合的, 所以这些方法通常称为运算符。比如 filter。

运算符也是高度可组合的,它们总是把数据作为输入并输出它们的结果,所以你可以用许多不同的方式轻松地将它们链接起来,实现比单个操作员自己能做的更多的事情。

调度程序是 dispatch queues 的 Rx 等价物。

RxSwift 将充当你的订阅(在左手边下面)和调度器(在右手边)之间的调度器,将工件发送到正确的上下文,并无缝地允许它们与彼此的输出一起工作。

RxSwift I:开始学习 RxSwift

要读取此关系图, 请在不同的计划程序中按预定的顺序 (1、2、3、…) 来执行彩色作品。例如:

·蓝色网络订阅以在基于自定义 NSOperation 的计划程序上运行的一段代码 (1) 开始。

·数据输出块作为下一个块 (2) 的输入, 它运行在一个不同的调度程序上, 它位于并发后台 GCD 队列中。

·最后, 在主线程调度程序上计划最后一块蓝色代码 (3), 以便用新数据更新 UI。

App architecture 应用的架构

值得一提的是,RxSwift 并没有以任何方式改变应用程序的架构;它主要处理事件、异步数据序列和通用通信协议。

通过在苹果开发文档中实现 MVC 体系结构,可以创建具有 Rx 的应用程序。如果你喜欢的话,你也可以选择实现 MVP 架构或 MVVM。RxSwift 也可以帮你实现自己的单向数据架构。

微软的 MVVM 架构是专门针对在平台上创建的事件驱动软件开发的,该平台提供数据绑定。RxSwift 和 MVVM 很好地结合在一起,在这本书的末尾,你会看到这个模式以及如何用 RxSwift 来实现它。

MVVM 和 RxSwift 结合在一起的原因是,ViewModel 允许您公开可观察的(Observable )属性,这些属性可以直接绑定到 View Controller 代码中的 UIKIT 控件。这使得绑定模型数据到 UI 非常简单地表示和编码:

RxSwift I:开始学习 RxSwift

本书中的所有其他示例都使用 MVC 架构来保持示例代码简单易懂。

RxCocoa

RxSwift 是通用 Rx API 的实现。因此,它不涉及任何 Cocoa 或 UIKit 类。

RxCocoa 是 RxSwift 的同伴库,所有的类都有助于 UIKit 和 Cocoa 的开发。除了具有一些高级类之外,RxCocoa 还为许多 UI 组件添加了响应式扩展,以便您可以订阅不同的 UI 事件。

例如,使用 RxCocoa 订阅 UISwitch 的状态变化是非常容易的,例如:

toggleSwitch.rx.isOn
	.subcribe(onNext: {enabled in
    	print(enabled ? "it's ON" : "it's OFF")
	})

RxCocoa adds the rx.isOn property (among others) to the UISwitch class so you can subscribe to generally useful event sequences.

RxCocoa 将 rx.isOn 属性(其中之一)添加到 UISwitch 类,这样您就可以订阅通常有用的事件序列。

RxSwift I:开始学习 RxSwift

安装

官方 git: https://github.com/ReactiveX/RxSwift

使用 CocoaPods 或者 Carthage 均很方便集成 RxSwift。

社区

RxSwift 社区非常友好,思想开放,并且热衷于讨论模式,常用技巧或互相帮助。

更多的 Rx 库和实验,像雨后春笋一样的涌现,可以在这里找到: https://github.com/RxSwiftCommunity

可能最好的方式来满足许多对 RxSwift 感兴趣的人,这是 Slack 的频道: http://rxswift-slack.herokuapp.com

Slack 频道拥有约 5000 名成员! 日常的主题包括:互相帮助,讨论 RxSwift 或其同伴库的潜在新功能,以及共享 RxSwift 博客文章和会议讲座。

第二章:Observables

Observable 是什么

Observable、observable sequence 和 sequence 在 Rx 都是一个意思。或者在其他 Rx 实现中称之为 stream。

最好称之为 Observable,不过翻译过来还是序列顺口些。

Observable 的生命周期

  • observable 发出包含元素的 next 事件。 它可以继续这样做,直到它:
  • …发出 error 事件并终止,或
  • …发出 completed 事件并终止。
  • 一旦 observable 被终止,它不能再发出事件。

将这种概念化的最佳方法之一是使用弹珠图( Marble Diagrams 基于时间轴上绘制的值)。

RxSwift I:开始学习 RxSwift

RxSwift I:开始学习 RxSwift

创建Observable

类型擦除的 ObservableType,也就是 Swift 中的泛型。

它代表了一种推式风格队列。

let observable: Observable = Observable .just(one)

订阅Observable

订阅 observable sequence 的事件处理程序。

func subscribe(_ on: @escaping (RxSwift.Event<Self.E>) -> Swift.Void) -> Disposable

let one = 1
let two = 2
let three = 3

let observable = Observable.of(one, two, three)

observable.subscribe(onNext: { element in
    print(element)
})

在指定的范围内生成一个整数的 observable sequence,使用指定的 scheduler 生成和发送观察者消息。

static func range(start: Self.E, count: Self.E, scheduler: ImmediateSchedulerType = default) -> RxSwift.Observable<Self.E>
let observable = Observable<Int>.range(start: 1, count: 10)
observable
    .subscribe(onNext: { i in
        let n = Double(i)
        let fibonacci = Int(((pow(1.61803, n) - pow(0.61803, n)) /
            2.23606).rounded())
        print(fibonacci)
    })

Disposing and terminating

请记住, 在收到订阅之前, Observable 的内容不会执行任何事情。它是触发一个 Observable 的开始发出事件的订阅, 直到它发出. error 或.completed 完成事件并终止。您可以通过取消对它的订阅来手动终止 Observable。

subscription.dispose()

Managing each subscription individually would be tedious, so RxSwift includes a DisposeBag type. A dispose bag holds disposables — typically added using the .disposed(by:) method — and will call dispose() on each one when the dispose bag is about to be deallocated.

单独管理每个订阅将是单调乏味的, 因此 RxSwift 引入 DisposeBag 类型。DisposeBag 持有 disposable 协议的对象,通常是使用.disposed(by:) 方法, 并将调用 dispose(), 当 DisposeBag 即将 deallocated。

let observable = Observable.of("A", "B", "C")

let subscription = observable.subscribe { event in
    print(event)
}
subscription.dispose()

create 操作符接受一个名为 subscribe 的参数。 它的工作是提供对可观察对象进行调用订阅的实现。 换句话说,它定义了将发送给订阅者的所有事件。

static func create(_ subscribe: @escaping (AnyObserver<String>) -> Disposable) -> Observable<String>

Creating observable factories

可以创建一个可观察的工厂,向每个订阅者发布一个新的 Observable,而不是创建一个等待订阅者的 Observable。

static func deferred(_ observableFactory: @escaping () throws -> Observable<Int>) -> Observable<Int>
let disposeBag = DisposeBag()

var flip = false

let factory: Observable<Int> = Observable.deferred {
    flip = !flip

    if flip {
        return Observable.of(1, 2, 3)
    } else {
        return Observable.of(4, 5, 6)
    }
}

for _ in 0...3 {
    factory.subscribe(onNext: {
        print($0, terminator: "")
    })
        .disposed(by: disposeBag)

    print()
}

Using Traits

Traits是具有比常规 Observable 更窄的行为集合的一种 Observable。它们的使用是可选的;您可以使用任何可能使用性状的规则观察。他们的目的是提供一种给你的 API 或者代码的读者更清楚地表达意图的方式。使用 Traits 暗示的上下文可以帮助您使代码更直观。

RxSwift 中有三种 Traits: Single, Maybe 和 Completable.

Singles 将发出.success(value)或.error 事件。 .success(value)实际上是.next 和.completed 事件的组合。 这对于一次性进程非常有用,它可以成功并产生一个值或失败,例如下载数据或从磁盘加载数据。

下面的例子是读取 Copyright.txt 的文件内容:

let disposeBag = DisposeBag()

enum FileReadError: Error {
    case fileNotFound, unreadable, encodingFailed
}

func loadText(from filename: String) -> Single<String> {
    return Single.create { single in
        let disposable = Disposables.create()

        guard let path = Bundle.main.path(forResource: filename, ofType: "txt") else {
            single(.error(FileReadError.fileNotFound))
            return disposable
        }

        guard let data = FileManager.default.contents(atPath: path) else {
            single(.error(FileReadError.unreadable))
            return disposable
        }

        guard let contents = String(data: data, encoding: .utf8) else {
            single(.error(FileReadError.encodingFailed))
            return disposable
        }

        single(.success(contents))

        return disposable
    }
}

loadText(from: "Copyright")
    .subscribe {
        switch $0 {
        case .success(let string):
            print(string)
        case .error(let error):
            print(error)
        }
    }
    .disposed(by: disposeBag)

Perform side effects

do操作员允许您插入副作用,处理程序执行过程中并不会以任何方式更改发出的事件的操作。

Print debug info

debug运算符, 它将打印 observable 的每个事件的信息。

第三章:Subjects

Subject 是什么

既可以作为发射者,也可以作为观察者,这就是所谓的 Subjects。

RxSwift 中有四个 subject 类型:

  1. PublishSubject: 开始为空, 只向订阅者发出新元素.
  2. BehaviorSubject: 从初始值开始, 将其重播或将最新的元素给新订阅者.
  3. ReplaySubject: 用缓冲区大小, 并将保持元素的缓冲区大小, 并将其重播到新订阅者.
  4. Variable:包装一个 BehaviorSubject,将其当前值保存为状态,并只将最新 / 初始值重播给新订阅者。

PublishSubject

/// Represents an object that is both an observable sequence as well as an observer.
///
/// Each notification is broadcasted to all subscribed observers.
public final class PublishSubject<Element>

当你只是想让订阅者只接受在订阅的时候以后发生的新的事件,直到他们 unsubscribe,或者 subject 已经 terminated 以.completed 或.error 事件的方式,PublishSubject 就可以派上用场。

BehaviorSubject

/// Represents a value that changes over time.
///
/// Observers can subscribe to the subject to receive the last (or initial) value and all subsequent notifications.
public final class BehaviorSubject<Element>

当您希望使用最新数据预先填充 View 时, BehaviorSubject 非常有用。例如, 可以将用户详情页中的控件绑定到 BehaviorSubject, 以便在应用程序获取新数据时, 可以使用最新值来预先填充显示。

RxSwift I:开始学习 RxSwift

ReplaySubject

/// Represents an object that is both an observable sequence as well as an observer.
///
/// Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies.
public class ReplaySubject<Element>

ReplaySubject 将临时缓存, 或缓冲区, 它们发出的最新元素, 由您选择的 specified 大小决定。然后, 他们会将该缓冲区重播到新订阅者。

Variable

/// Variable is a wrapper for `BehaviorSubject`.
///
/// Unlike `BehaviorSubject` it can't terminate with error, and when variable is deallocated
/// it will complete its observable sequence (`asObservable`).
///
/// **This concept will be deprecated from RxSwift but offical migration path hasn't been decided yet.**
/// https://github.com/ReactiveX/RxSwift/issues/1501
///
/// Current recommended replacement for this API is `RxCocoa.BehaviorRelay` because:
/// * `Variable` isn't a standard cross platform concept, hence it's out of place in RxSwift target.
/// * It doesn't have a counterpart for handling events (`PublishRelay`). It models state only.
/// * It doesn't have a consistent naming with *Relay or other Rx concepts.
/// * It has an inconsistent memory management model compared to other parts of RxSwift (completes on `deinit`).
///
/// Once plans are finalized, official availability attribute will be added in one of upcoming versions.

public final class Variable<Element> {

Variable 封装 BehaviorSubject 并将其当前值存储为状态。您可以通过其 value 属性访问当前值,并且,与一般的其他 Subject 和 Observable 不同,您还可以使用 value 属性将新元素设置到变量上。换句话说,你不需要使用 onNext(_:)。

let variable = Variable("Initial value")
let disposeBag = DisposeBag()
variable.value = "New initial value"
variable.asObservable()
  .subscribe {
    print(label: "1)", event: $0)
  }
  .disposed(by: disposeBag)

variable.value = "1"
variable.asObservable()
  .subscribe {
    print(label: "2)", event: $0)
  }
  .disposed(by: disposeBag)

variable.value = "2"

第四章:Observables 和 Subjects 的实践

本章重点是在一个完整的应用开发中使用 RxSwift,一步一步的学会如何把概念应用到实际项目中。

在 view controller 中使用 variable

override func viewDidLoad() {
    super.viewDidLoad()

    images.asObservable()
      .subscribe(onNext: { [weak self] photos in
        guard let preview = self?.imagePreview else { return }
        preview.image = UIImage.collage(images: photos,
                                        size: preview.frame.size)
      })
      .disposed(by: bag)

    images.asObservable()
      .subscribe(onNext: { [weak self] photos in
        self?.updateUI(photos: photos)
      })
      .disposed(by: bag)
  }

使用 variable 在 view controller 之间传值

view controller 之间传值可以通过 delegate,但是用 subject 更好。

RxSwift I:开始学习 RxSwift

RxSwift I:开始学习 RxSwift

@IBAction func actionAdd() {
    let photosViewController = storyboard!.instantiateViewController(
      withIdentifier: "PhotosViewController") as! PhotosViewController

    photosViewController.selectedPhotos
      .subscribe(onNext: { [weak self] newImage in
        guard let images = self?.images else { return }
        images.value.append(newImage)
      }, onDisposed: {
          print("completed photo selection")
      })
      .disposed(by: bag)

    navigationController!.pushViewController(photosViewController, animated: true)
  }

封装已有 API 为 Observable

import Foundation
import UIKit
import Photos

import RxSwift

class PhotoWriter {
  enum Errors: Error {
    case couldNotSavePhoto
  }

  static func save(_ image: UIImage) -> Observable<String> {
    return Observable.create({ observer in
      var savedAssetId: String?
      PHPhotoLibrary.shared().performChanges({
        let request = PHAssetChangeRequest.creationRequestForAsset(from: image)
        savedAssetId = request.placeholderForCreatedAsset?.localIdentifier
      }, completionHandler: { success, error in
        DispatchQueue.main.async {
          if success, let id = savedAssetId {
            observer.onNext(id)
            observer.onCompleted()
          } else {
            observer.onError(error ?? Errors.couldNotSavePhoto)
          }
        }
      })
      return Disposables.create()
    })
  }
}

RxSwift traits 实践

  1. Single 只有 .success.error 两种事件 。适合作为封装网络接口的返回值,要么成功,要么失败。 RxSwift I:开始学习 RxSwift
PhotoWriter.save(image)
      .asSingle()
      .subscribe(onSuccess: { [weak self] id in
        self?.showMessage("Saved with id: \(id)")
        self?.actionClear()
        }, onError: { [weak self] error in
          self?.showMessage("Error", description: error.localizedDescription)
      })
      .disposed(by: bag)

2 . Maybe 有三种事件: .success.completed.error

和 Single 一样,你既可以通过 Maybe.create({...}) 直接创建,也可以使用 .asMaybe()

RxSwift I:开始学习 RxSwift

  1. Completable 只有 .completed.error 两种事件。适合只关心是否完成,而不需要传递 value 的情况。使用方法: Completable.create({...})

RxSwift I:开始学习 RxSwift


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

谷歌时代的柏拉图

谷歌时代的柏拉图

[美] 丽贝卡·戈尔茨坦 / 李鹏程 / 中信出版集团·新思文化 / 2017-12-10 / 69.00元

我愿意用我所有的科技去换取和苏格拉底相处的一个下午。 ——史蒂夫•乔布斯 谷歌时代,科技昌明,众声喧哗,哲学提出的许多问题,科学似乎都已经给出了答案。若是如此,为什么我们今天还需要哲学?这个由古希腊城邦时代的哲人苏格拉底和柏拉图开创的学科,真的过时了吗? 已经2400岁 的柏拉图对此有话要说。哲学家兼小说家、美国国家人文奖章获得者戈尔茨坦史海钩沉,从经典著作中复活了柏拉图,让他来......一起来看看 《谷歌时代的柏拉图》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具