iOS.& Swift Books RXSWIFT:SWIFT的无功

2
可观察到 由斯科特加德纳撰写

既然你已经了解了RXSWIFT的一些基本概念,现在是时候跳跃并与观察品一起玩。

在本章中,您将介绍创建和订阅观察到的几个示例。真实世界使用一些可观察到的观察可能似乎有点晦涩难懂,但请放心,您将获得重要技能,并在rxSwift中获取很多关于您可用的观察类型类型。你将在本书的其余部分使用这些技能 - 而且!

入门

对于本章,您将使用已经设置为包含游乐场和rxSwift框架的Xcode项目。要开始,请打开终端应用程序,导航到本章的入门文件夹,然后导航到此 rxplayground. project folder. Finally, run the bootstrap.sh script by entering this command:

./bootstraph.sh

引导过程需要几秒钟;请记住,每次您要打开此游乐场项目时,您都需要重复上述步骤。您不能直接打开游乐场文件或工作区。

选择 rxswiftplayground. 在里面 项目导航员,而且你应该看到以下内容:

扭转游乐场页面,通过 来源 文件夹在 项目导航员,选择 supportCode.swift.. It contains the following helper function example(of:):

public func example(of description: String, action: () -> Void) {
  print("\n--- Example of:", description, "---")
  action()
}

当您通过本章工作时,您将使用此功能封装不同的示例。您将看到如何不久使用此功能。

但在你深深的时候,现在可能是回答问题的好时机:什么 an observable?

什么是可观察的?

可观察到 是Rx的核心。您将花一些时间讨论观察到的内容,如何创建它们,以及如何使用它们。

您将看到“可观察”,“可观察序列”和“序列”在Rx中可互换使用。而且,真的,他们都是一样的。您甚至可能会看到偶尔不时抛出的“流”,特别是从不同的无功编程环境中到达伦夫特的开发人员。 “流”也指的是同样的事情,但是,在伦威特,所有酷孩子都称之为序列,而不是流。在rxswift ...

......或者的东西 作品 with a sequence. And an Observable 是 just a sequence, with some special powers. One of these powers — in fact the most important one — is that it is 异步。观察到在一段​​时间内产生事件,这被称为 发出 。事件可以包含值,例如自定义类型的数字或实例,或者它们可以被识别的手势,例如抽头。

概念化这一方法之一是通过使用大理石图,这只是绘制时间轴上的值。

左右箭头表示时间,编号圆圈表示序列的元素。元素1将被发射,一段时间将通过,然后将发出2和3。你问多少时间?它可以在 任何 点在观察到的一生中 - 这会给你带到下一个话题,你会学到关于:可观察到的生命周期。

可观察的生命周期

在以前的大理石图中,可观察到的三个元素。当可观察到的散发元素时,它会在被称为a中 下一个 event.

这是另一个大理石图,这次包括垂直条,表示这种可观察到的道路末端。

这可观察到的是三个龙头事件,然后结束。这被称为a 完全的 事件,就是这样 终止。例如,也许水龙头在被解雇的视图上。重要的是可观察到的终止,并且不再能发出任何东西。这是正常的终端。但是,有时事情会出错。

在这个大理石图中发生了错误,由红色x表示。可观察到的发射 错误 包含错误的事件。这与可观察到的通常与a相同 完全的 事件。如果可观察到的散发 错误 事件,它也被终止,不再能发出其他任何东西。

这是一个快速回顾:

  • 可观察的散发 下一个 包含元素的事件。
  • 它可以继续这样做,直到一个 终止事件 被排放,即,一个 错误 或者 完全的 event.
  • 一旦可观察到终止,它就无法再发出事件。

事件表示为枚举案例。这是rxswift源代码中的实际实现:

/// Represents a sequence event.
///
/// Sequence grammar: 
/// **next\* (error | completed)**
public enum Event<Element> {
    /// Next element is produced.
    case next(Element)

    /// Sequence terminated with an error.
    case error(Swift.Error)

    /// Sequence completed successfully.
    case completed
}

Here, you see that 下一个 events contain an instance of some Element, 错误 events contain an instance of Swift.Error完全的 events are simply stop events that don’t contain any data.

既然你了解可观察到的是什么以及它所做的,你会创建一些观察到,以便在行动中看到它们。

创建观察到

从当前文件切换回 rxswiftplayground. 并添加以下代码:

example(of: " 只是 , of, from") {
  // 1
  let one = 1
  let two = 2
  let three = 3
  
  // 2
  let observable = Observable<Int>.just(one)
}

在上面的代码中,你:

  1. 定义将在以下示例中使用的整数常量。
  2. Create an observable sequence of type Int using the 只是 method with the one integer constant.

The 只是 method is aptly named, because all it does is create an observable sequence containing 只是 a single element. It is a static method on Observable. However, in Rx, methods are referred to as “operators.”And the eagle-eyed among you can probably guess which operator you’re going to check out next.

将此代码添加到同一示例中:

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

这次,您没有明确声明类型。你 可能 think, because you give it several integers, the type is Observable of [Int].

然而, 选项 - 单击 on observable2 to show its inferred type, and you’ll see that it’s an Observable of Int, not an array:

That’s because the of operator has a oriadic parameter, and Swift can infer the Observable’s type based on it.

Pass an array to of when you want to create an observable array. 将此代码添加到示例的底部:

let observable3 = Observable.of([one, two, three])

选项 - 单击 on observable3 和 you’ll see that it is indeed an Observable of [Int]. The 只是 operator can also take an array as its single element, which may seem a little weird at first. However, it’s the 大批 这是单个元素,而不是其内容。

Another operator you can use to create observables is from. 将此代码添加到示例的底部:

let observable4 = Observable.from([one, two, three])

The from operator creates an observable of individual elements from an array of typed elements. Option-click on observable4 和 you’ll see that it is an Observable of Int, not [Int]. The from operator 只要 takes an array.

你的控制台可能看起来很秃头。那是因为除了示例标题之外你还没有打印任何东西。是时候改变了 订阅 to observables.

订阅观察到

From your experience as an iOS developer, you are likely familiar with NotificationCenter; it broadcasts notifications to observers. However, these observed notifications are different than RxSwift Observables. Here’s an example of an observer of the UIKeyboardDidChangeFrame notification, with a handler as a trailing closure (don’t add this code to your playground):

let observer = NotificationCenter.default.addObserver(
  forName: UIResponder.keyboardDidChangeFrameNotification,
  object: nil,
  queue: nil) { notification in
  // Handle receiving notification
}

订阅rxswift可观察到的是相当相似的;你叫观察可观察 订阅 to it. So instead of addObserver(), you use subscribe(). Unlike NotificationCenter, where developers typically use only its .default singleton instance, each observable in Rx is different.

更重要的是,可观察到的不会发送事件,或执行任何工作,直到它有订阅者。

Remember, an observable is really a sequence definition, and subscribing to an observable is really more like calling 下一个 () on an Iterator 在里面 Swift standard library (don’t add this code to your playground):

let sequence = 0..<3

var iterator = sequence.makeIterator()

while let n = iterator.next() {
  print(n)
}

/* Prints:
 0
 1
 2
 */

订阅观察到 is more streamlined. You can also add handlers for each event type an observable can emit. Recall that an observable emits 下一个 , 错误 完全的 events. A 下一个 event passes the element being emitted to the handler, and an 错误 event contains an error instance.

要在操作中查看此项,请将此新示例添加到您的游乐场。请记住,自己插入每个新示例, 前一个例子的闭合卷曲括号。

example(of: "subscribe") {
  let one = 1
  let two = 2
  let three = 3
  
  let observable = Observable.of(one, two, three)
}

This is similar to the previous example, except this time you’re using the of operator. Now add this code at the bottom of 这个 示例,订阅可观察到的:

observable.subscribe { event in
  print(event)
}

笔记 :控制台应在输出时自动显示,但您可以通过选择手动显示它 查看▸调试区域▸激活控制台 from the menu. This is where the print statements in the playground display their output.

选项 - 单击 on the subscribe operator, and observe it takes a closure parameter that receives an Event of type Int 和 doesn’t return anything, and subscribe returns a Disposable. You’ll learn about disposables shortly.

This subscription will print out each event emitted by observable:

--- Example of: subscribe ---
next(1)
next(2)
next(3)
completed

The observable emits a 下一个 event for each element, then emits a 完全的 event and is terminated. When working with observables, you’ll usually be primarily interested in the 元素 emitted by 下一个 events, rather than the events themselves.

要看到直接访问元素的一种方法,请使用以下代码从上面替换订阅代码:

observable.subscribe { event in
  if let element = event.element {
    print(element)
  }
}

Event has an element property. It’s an optional value, because only 下一个 events have an element. So you use optional binding to unwrap the element if it’s not nil. Now only the elements are printed, not the events 包含 the elements, nor the 完全的 event:

1
2
3

That’s a nice pattern, and it’s so frequently used that there’s a shortcut for it in RxSwift. There’s a subscribe operator for each type of event an observable emits: 下一个 , 错误 完全的.

替换以前的订阅代码:

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

笔记 :如果在Xcode首选项中打开了代码完成建议,则可能会要求您为其他事件的处理程序询问。现在忽略这些。

Now you’re only handling 下一个 event elements and ignoring everything else. The onNext closure receives the 下一个 event’s element as an argument, so you don’t have to manually extract it from the event like you did before.

现在您知道如何创建一个元素和许多元素的可观察到。但可观察到的是什么 elements? The 空的 operator creates an empty observable sequence with zero elements; it will only emit a 完全的 event.

将此新示例添加到您的游乐场:

example(of: " 空的 ") {
  let observable = Observable<Void>.empty()
}

An observable must be defined as a specific type if it cannot be inferred. So the type must be explicitly defined, because 空的 has nothing from which to infer the type. Void 是 typically used because nothing is going to be emitted.

将此代码添加到示例以订阅空白可观察者:

observable.subscribe(
  // 1
  onNext: { element in
    print(element)
  },
  
  // 2
  onCompleted: {
    print("Completed")
  }
)

在上面的代码中,你:

  1. Handle 下一个 events, just like you did in the previous example.
  2. Simply print a message, because a .completed event does not include an element.

In the console, you’ll see that 空的 只要 emits a .completed event:

--- Example of: empty ---
Completed

什么使用是 空的 可观察吗?当您想返回一个立即终止或有意具有零值的可观察到值时,它们可以方便。

As opposed to the 空的 operator, the 绝不 operator creates an observable that doesn’t emit anything and 绝不 终止。它可以用来表示无限持续时间。将此示例添加到您的游乐场:

example(of: " 绝不 ") {
  let observable = Observable<Void>.never()
  
  observable.subscribe(
    onNext: { element in
      print(element)
    },
    onCompleted: {
      print("Completed")
    }
  )
}

Nothing is printed, except for the example header. Not even "Completed". How do you know if this is even working? Hang on to that inquisitive spirit until the 挑战 section.

到目前为止,您已使用特定元素或价值观的可观察性。然而,也可以从一系列值生成可观察到的。

将此示例添加到您的游乐场:

example(of: "range") {
  // 1
  let observable = Observable<Int>.range(start: 1, count: 10)
  
  observable
    .subscribe(onNext: { i in  
      // 2
      let n = Double(i)
      
      let fibonacci = Int(
        ((pow(1.61803, n) - pow(0.61803, n)) /
          2.23606).rounded()
      )
      
      print(fibonacci)
  })
}

你刚做了什么:

  1. Create an observable using the range operator, which takes a start integer value and a count of sequential integers to generate.
  2. 计算和打印 NTH. 每个发出元素的斐波纳契号。

There’s actually a better place than in the onNext handler to put code that transforms the emitted element. You’ll learn about that in Chapter 7, “转变运营商.”

Except for the 绝不 () example, up to this point you’ve worked with observables that automatically emit a 完全的 event and naturally terminate. Doing so allowed you to focus on the mechanics of creating and subscribing to observables, but this brushed an important aspect of subscribing to observables under the carpet. It’s time to do some housekeeping before moving on.

处理和终止

Remember that an observable doesn’t do anything until it receives a subscription. It’s the subscription that triggers an observable’s work, causing it to emit new events until an 错误 或者 完全的 event terminates the observable. However, you can also manually cause an observable to terminate by canceling a subscription to it.

将此新示例添加到您的游乐场:

example(of: " 赔货 ") {
  // 1
  let observable = Observable.of("A", "B", "C")
  
  // 2
  let subscription = observable.subscribe { event in
    // 3
    print(event)
  }
}

简直:

  1. 创建一个字符串的可观察。
  2. Subscribe to the observable, this time saving the returned Disposable as a local constant called subscription.
  3. Print each emitted event 在里面 handler.

To explicitly cancel a subscription, call 赔货 () on it. After you cancel the subscription, or 赔货 其中,目前示例中的可观察将停止发出事件。

将此代码添加到示例的底部:

subscription.dispose()

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

将此新示例添加到您的游乐场:

example(of: "DisposeBag") {
  // 1
  let disposeBag = DisposeBag()
  
  // 2
  Observable.of("A", "B", "C")
    .subscribe { // 3
      print($0)
    }
    .disposed(by: disposeBag) // 4
}

逐步,你:

  1. 创建一个处置袋子。
  2. 创造一个观察者。
  3. Subscribe to the observable and print out the emitted events using the default argument name $0.
  4. Add the returned Disposable from subscribe to the dispose bag.

这是您最常使用的模式:创建和订阅可观察,并立即将订阅添加到Dispose Bag中。

为什么要打扰一次性品?

If you forget to add a subscription to a dispose bag, or manually call 赔货 on it when you’re done with the subscription, or in some other way cause the observable to terminate at some point, you will 大概 leak memory.

如果你忘记,不要担心; Swift编译器应警告您有关未使用的一次性产品。

In the previous examples, you created observables with specific 下一个 event elements. Using the create operator is another way to specify all the events an observable will emit to subscribers.

将此新示例添加到您的游乐场:

example(of: "create") {
  let disposeBag = DisposeBag()
  
  Observable<String>.create { observer in
    
  }
}

The create operator takes a single parameter named subscribe. Its job is to provide the implementation of calling subscribe on the observable. In other words, it defines all the events that will be emitted to subscribers.

If you option-click on create right now you will not get the Quick Help documentation, because this code won’t compile yet. So here’s a preview:

The subscribe parameter is an escaping closure that takes an AnyObserver 和 returns a Disposable. AnyObserver 是 a generic type that facilitates adding values 一种可观察的序列,然后将被发射到用户。

Change the implementation of create to the following:

Observable<String>.create { observer in
  // 1
  observer.onNext("1")
  
  // 2
  observer.onCompleted()
  
  // 3
  observer.onNext("?")
  
  // 4
  return Disposables.create()
}

以下是您对此代码的作用:

  1. Add a 下一个 event onto the observer. onNext(_:) 是 a convenience method for on(.next(_:)).
  2. Add a 完全的 event onto the observer. Similarly, onCompleted 是 a convenience method for on(.completed).
  3. Add another 下一个 event onto the observer.
  4. 返回一次性,定义当您的可观察者终止或处置时会发生什么;在这种情况下,不需要清除,因此您返回一个空的一次性。

笔记 : The last step, returning a Disposable, may seem strange at first. Remember that subscribe operators must return a disposable representing the subscription, so you use Disposables.create() to create a disposable.

Do you think the second onNext element (?) could ever be emitted to subscribers? Why or why not?

To see if you guessed correctly, subscribe to the observable by adding the following code on the next line after the create implementation:

.subscribe(
  onNext: { print($0) },
  onError: { print($0) },
  onCompleted: { print("Completed") },
  onDisposed: { print("Disposed") }
)
.disposed(by: disposeBag)

You subscribed to the observable, and implemented all the handlers using default argument names for element and error arguments passed to the onNextonError handlers, respectively. The result is, the first 下一个 event element, "Completed""Disposed" are printed out. The second 下一个 event is not printed because the observable emitted a 完全的 event and terminated before it is added.

--- Example of: create ---
1
Completed
Disposed

What would happen if you add an error to the observer? Add this code at the top of the example to define an error type with a single case:

enum MyError: Error {
  case anError
}

Next, add the following line of code between the observer.onNextobserver.onCompleted calls:

observer.onError(MyError.anError)

现在可观察到的发出错误,然后终止:

--- Example of: create ---
1
anError
Disposed

What would happen if you did not add a 完全的 或者 错误 event, and also didn’t add the subscription to 赔货 Bag? Comment out the observer.onError, observer.onCompleted 赔货 d(by: disposeBag) lines of code to find out.

这是完整的实现:

example(of: "create") {
  enum MyError: Error {
    case anError
  }
  
  let disposeBag = DisposeBag()
  
  Observable<String>.create { observer in
    // 1
    observer.onNext("1")
    
//    observer.onError(MyError.anError)
    
    // 2
//    observer.onCompleted()
    
    // 3
    observer.onNext("?")
    
    // 4
    return Disposables.create()
  }
  .subscribe(
    onNext: { print($0) },
    onError: { print($0) },
    onCompleted: { print("Completed") },
    onDisposed: { print("Disposed") }
  )
//  .disposed(by: disposeBag)
}

恭喜,你只是泄露了内存!可观察者永远不会完成,并且永远不会处理一次性。

--- Example of: create ---
1
?

Feel free to uncomment the line that adds the 完全的 event or the code that adds the subscription to the 赔货 Bag if you just can’t stand to leave this example in a leaky state.

创造可观察的工厂

不是创建等待用户等待的可观察者,可以创建可观察到的工厂,该工厂为每个订户提供了新的可观察到的。

将此新示例添加到您的游乐场:

example(of: "deferred") {
  let disposeBag = DisposeBag()
  
  // 1
  var flip = false
  
  // 2
  let factory: Observable<Int> = Observable.deferred {
    
    // 3
    flip.toggle()
    
    // 4
    if flip {
      return Observable.of(1, 2, 3)
    } else {
      return Observable.of(4, 5, 6)
    }
  }
}

从顶部,你:

  1. Create a Bool flag to flip which observable to return.
  2. Create an observable of Int factory using the deferred operator.

$ 3. Toggle flip, which happens each time factory 是 subscribed to. 4. Return different observables based on whether fliptrue 或者 false.

Externally, an observable factory is indistinguishable from a regular observable. Add this code to the bottom of the example to subscribe to factory four times:

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

  print()
}

Each time you subscribe to factory, you get the opposite observable. In other words, you get 123, then 456, and the pattern repeats each time a new subscription is created:

--- Example of: deferred ---
123
456
123
456

使用特征

特征是具有比常规可观察可观察到的较窄行为集的可观察到。他们的使用是可选的;您可以使用常规可观察到的任何地方您可能使用特质。他们的目的是提供一种方法来更清楚地向您的代码或API的消费者的读者传达意图。使用特性隐含的上下文可以帮助您更直观地制作代码。

There are three kinds of traits in RxSwift: Single, MaybeCompletable. Without knowing anything more about them yet, can you guess how each one is specialized?

Singles will emit either a success(value) 或者 错误 (error) 事件。 success(value) 是 actually a combination of the 下一个 完全的 events. This is useful for one-time processes that will either succeed and yield a value or fail, such as when downloading data or loading it from disk.

A Completable will only emit a 完全的 或者 错误 (error) 事件。 It will not emit any values. You could use a completable when you only care that an operation completed successfully or failed, such as a file write.

Finally, Maybe 是 a mashup of a SingleCompletable. It can either emit a success(value), 完全的 或者 错误 (error). If you need to implement an operation that could either succeed or fail, and optionally return a value on success, then Maybe 是 your ticket.

您将有机会在第4章中的特征工作,“观察到&实践中的主题,“及以后。目前,您将通过使用单个以命名的文本文件加载某些文本的基本示例来运行 版权所有.txt. 在里面 资源 这个游乐场的文件夹 - 因为谁偶尔爱一些法律呢?

将此示例添加到您的游乐场:

example(of: "Single") {
  // 1
  let disposeBag = DisposeBag()

  // 2
  enum FileReadError: Error {
    case fileNotFound, unreadable, encodingFailed
  }
  
  // 3
  func loadText(from name: String) -> Single<String> {
    // 4
    return Single.create { single in
    
    }
  }
}

在上面的代码中,你:

  1. 创建一个稍后使用的Dispose Bag。
  2. Define an Error enum to model some possible errors that can occur in reading data from a file on disk.
  3. Implement a function to load text from a file on disk that returns a Single.
  4. Create and return a Single.

Add this code inside the create closure to complete the implementation:

// 1
let disposable = Disposables.create()

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

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

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

// 5
single(.success(contents))
return disposable

使用此代码,您:

  1. Create a Disposable, because the subscribe closure of create expects it as its return type.
  2. Get the path for the filename, or else add a file not found error onto the Single 和 return the disposable you created.
  3. Get the data from the file at that path, or add an unreadable error onto the Single 和 return the disposable.
  4. Convert the data to a string; otherwise, add an encoding failed error onto the Single 和 return the disposable. Starting to see a pattern here?
  5. Made it this far? Add the contents onto the Single as a success, and return the disposable.

现在,您可以将此功能置于工作。将此代码添加到示例:

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

在这里,你:

  1. Call loadText(from:) 和 pass the root name of the text file.
  2. Subscribe to the Single it returns.
  3. 如果成功,请打开事件并打印字符串,或者如果不是,则打印错误。

您应该从打印到控制台的文件中查看文本,这与游乐场底部的版权评论相同:

--- Example of: Single ---
Copyright (c) 2020 Razeware LLC
...

尝试将文件名更改为其他内容,您应该看到未找到未发现错误的文件。

挑战

练习 永恒的。通过完成这些挑战,您将练习在本章中学到的内容,并在与可观察品一起使用的内容中获取更多的知识。

为每个挑战提供了初学游戏工作空间以及它的完成版本。享受!

挑战1:执行副作用

In the 绝不 operator example earlier, nothing printed out. That was before you added your subscriptions to dispose bags. However, if you added a subscription to a dispose bag, you also could’ve printed out a message in subscribe’s onDisposed handler.

当您想执行不影响可观察到的发出事件的某些方面的工作时,还有另一个有用的操作员。

The do operator enables you to insert 副作用; that is, handlers to do things that will not change the emitted event in any way. Instead, do will just pass the event through to the next operator in the chain. Unlike subscribe, do also includes an onSubscribe handler.

The method for using the do operator is do(onNext:onError:onCompleted:onSubscribe:onDispose). Each parameter is optional and defaults to nil, so you can provide handlers for just the events you’re interested in. Use Xcode’s autocompletion to get the closure parameters for each of the events.

To complete this challenge, insert the do operator in the 绝不 example using the onSubscribe handler. Feel free to include any of the other handlers; they work just like subscribe’s handlers do.

虽然你在它,创建一个处置袋并将订阅添加到它。

不要忘记你可以随时偷看完成的挑战游乐场“灵感”。

挑战2:打印调试信息

Performing side effects is one way to debug your Rx code. But it turns out that there’s even a better utility for that purpose: the debug operator, which will print information about every event for an observable.

It has several optional parameters, but perhaps the most useful one let’s you include an identifier string that is also printed out. In complex Rx chains, where you might add debug calls in multiple places, this can really help differentiate the source of each printout.

Continuing to work in the playground from the previous challenge, complete this challenge by replacing the use of the do operator with debug 和 provide a string identifier to it as a parameter. Observe the debug output in Xcode’s console.

有一个技术问题?想报告一个错误吗? 您可以向官方书籍论坛中的书籍作者提出问题和报告错误 这里 .

有反馈分享在线阅读体验吗? 如果您有关于UI,UX,突出显示或我们在线阅读器的其他功能的反馈,您可以将其发送到设计团队,其中表格如下所示:

© 2021 Razeware LLC