新的 2021 年高兴!这是恰当艰难的一年,我相信咱们都期望有更好的一年,咱们总算能够过上正常的生活。
新年伊始,还有什么比发布新版别更好的工作呢?向 RxSwift 6 问好。
这篇文章将向你快速概述一些或许影响你的最值得留意的变化。
留意:这仅仅一些风趣更新的部分列表,显然不包括很多较小的过错修正和改善。
有关完好的更改日志,请检查 release notes。
话不多说,让咱们直接开端吧!
新 Logo
当然,这不是技术上的改动,但绝对值得一提。
RxSwift 一直运用 Reactive Extensions 的原始 Volta Eel 徽标,但我觉得这个主要版别或许是一个很好的机会,能够为 RxSwift 的徽标添加一些共同的优势。
这是一个以其自己的精神和身份使其绝无仅有的机会,一同依然尊重原始的 ReactiveX 徽标以及 Swift 的徽标。
我给你,新的 RxSwift Logo!
将 Binder
从 RxCocoa 迁移到 RxSwift
这是一个细小但强烈要求的改动,而且是有意义的。 Binder
,顾名思义,答应你界说一种将 Observable
可调查序列绑定到其间的办法,以反响性地供给绑定的输入。
例如:
viewModel.isButtonEnable.bind(to: myButton.rx.isEnabled)
运用底层 Binder
让你绑定到 rx.isEnabled
Binder
一直存在于 RxCocoa 内部,但咱们社区的用例和各种讨论表明,它是一个超级有用的实体,能够为更广泛的 RxSwift 受众供给服务,因而它现在是其间的一部分,而且 RxCocoa 不需要运用 Binder。
运用 @dynamicMemberLookup
主动树立 Binder
s
RxSwift 包含一个名为 .rx
的命名空间,它答应你为特定目标放置自己的反响式扩展。
例如,给定一个如下所示的自界说 MyView
:
class MyView: UIView {
var title: String
var subtitle: String?
var icon: UIImage?
}
创建反响式绑定的常见模式一般如下所示:
extension Reactive where Base: MyView {
var title: Binder<String> {
Binder(base) { base, title in
base.title = title
}
}
var subtitle: Binder<String?> {
Binder(base) { base, subtitle in
base.subtitle = subtitle
}
}
var icon: Binder<UIImage?> {
Binder(base) { base, icon in
base.icon = icon
}
}
}
这将答应你将恰当类型的可调查序列绑定到各种反响性输入:
viewModel.title.bind(to: myView.rx.title)
viewModel.subtitle.bind(to: myView.rx.subtitle)
viewModel.icon.drive(myView.rx.icon)
这十分有用,甚至 RxCocoa 自身也是如此为其顾客供给响应式绑定的。
不幸的是,这种代码也恰当重复和样板化。它真正所做的仅仅反映底层 Base 的属性。
幸运的是,从 Swift 5.1 开端,咱们针对这个问题有了更好的解决计划 ——@dynamicMemberLookup
。
RxSwift 6 将主动为任何类组成一切这些 Binder
,这意味着我上面展现的一切 Binder
代码都能够彻底删去,并真正整理您的代码。
只需开端在任何 AnyObject
承继类上编写 .rx
,你就会立即看到扩展根底目标的每个属性的主动组成绑定器:
不过不必忧虑,您自己的自界说反响式扩展依然优先于组成的动态成员扩展,这使您能够进行更精细的控制。
RxSwift 新增 withUnretained
运用 RxSwift 和 Cocoa /iOS 代码时的常见模式是获取对 self
的弱引用,以便您能够将宣布的值传递给一切者,例如:
viewModel.importantInfo
.subscribe(onNext: { [weak self] info in
guard let self = self else { return }
self.doImportantTask(with: info)
})
.disposed(on: disposeBag)
留意:请当心将此运算符与缓冲运算符(例如
share(replay: 1)
)一同运用,因为它还会缓冲保留的目标,这或许会导致引用循环。如果您想要更简略的替代计划,请检查 RxSwift 6.1 中的subscribe(with:onNext:onError:onCompleted:onDispose:)
。
关于单个输出来说,这似乎很好,但想象一下它在单个代码库中呈现的频率。
幸运的是,RxSwiftExt 是一个社区项目,它拥有不属于 RxSwift 自身的各种附加运算符,它有一个适合这种状况的运算符,称为 withUnretained
。它最初是由一位好朋友、iOS 演讲者 Vincent Pradeilles 实现的。
因为该运算符的受欢迎程度以及该用例的普遍性,因而将其引进 RxSwift 自身是有意义的。
从 RxSwift 6 开端,您能够像这样重写上面的代码:
viewModel.importantInfo
.withUnretained(self) // Tuple of (Object, Element)
.subscribe(onNext: { owner, info in
owner.doImportantTask(with: info)
})
.disposed(by: disposeBag)
洁净多了!
Infallible
Infallible
是一种新型流,与 Observable
相同,只要一个差异 – 它确保不会失败。这意味着你不能从中宣布过错事情,这是由编译器确保的。
例如,您能够运用 Infallible.create
创建一个相似于 Observable.create
的目标:
Infallible<String>.create { observer in
observer(.next("Hello"))
observer(.next("World"))
observer(.completed)
// No way to error here
return Disposables.create {
// Clean-up
}
}
请留意,您只能传递 .next(Element)
或 .completed
事情。你没有办法让这个可调查序列失败。一切其他环绕 Infallible
工作的运算符都具有相同的确保(例如,您不能调用 Infallible.error
而不是 Observable.error
)
如果您运用过 RxCocoa,您或许会想 — 嘿,Driver
和 Signal
之间有什么差异?
首先,Infallible
坐落 RxSwift 中,而别的两个坐落 RxCocoa 中。但更重要的是,Driver
和 Signal
一直运用 MainScheduler
并共享资源(运用 share()
)。 Infallible
的状况并非如此,它彻底是一个根底的可调查序列,仅具有编译时确保无误性的功用。
Observable<Data>
的新解码 (type:decoder:
) 运算符
RxSwift 6 添加了一个解码运算符,专门用于宣布数据的 Observables,相似于兼并:
service.rx
.fetchJSONUsers() // Observable<Data>
.decode(type: [User].self, decoder: JSONDecoder()) // Observable<[User]>
可变参数 drive()
和 emit()
RxSwift 5 引进了可变参数绑定,它能够让您履行以下操作:
viewModel.string.bind(to: input1, input2, input3)
RxSwift 6 现在为 Driver
和 Signal
带来了相同的可变参数绑定 – 运用可变参数驱动和宣布操作符:
viewModel.string.drive(input1, input2, input3)
viewModel.number.emit(input4, input5)
Single
现在更好地遵从 Swift 的 Result
直到 RxSwift 5,Single
有一个自界说事情:
public enum SingleEvent<Element> {
case success(Element)
case error(Swift.Error)
}
如果您看到这个并说,嘿 – 这看起来很像 Result<Element, Swift.Error>
,那么你是对的!
从 RxSwift 6 开端,SingleEvent
仅仅 Result<Element, Swift.Error>
的别号。
这一变化也体现在其他 API 中,例如 subscribe
:
// RxSwift 5
single.subscribe(
onSuccess: { value in
print("Got a value: (value)")
},
onError: { error in
print("Something went wrong: (error)")
}
)
// RxSwift 6
single.subscribe(
onSuccess: { value in
print("Got a value: (value)")
},
onFailure: { error in
print("Something went wrong: (error)")
}
)
支撑 keypath 的新的 uniqueUntilChange(at:)
运算符
uniqueUntilChanged
是一个超级有用的运算符,它能够让您删去相同的值排放以避免浪费地处理它们。
例如:
myStream.distinctUntilChanged { $0.searchTerm == $1.searchTerm }
这是要害途径十分有用的另一种状况。从 RxSwift 6 开端,您能够简略地编写:
myStream.distinctUntilChanged(at: .searchTerm)
新的 ReplayRelay
Relay 环绕 Subject,让您以仅处理值的方式中继消息,因为中继确保永久不会失败或完结。
ReplayRelay
是 RxSwift 6 的最新弥补,除了现有的 BehaviorRelay
和 PublishRelay
之外,它还包装了 ReplaySubject
。
创建一个运用与创建 ReplaySubject
彻底相同的接口:
// Subject
ReplaySubject<Int>.create(bufferSize: 3)
// Relay
ReplayRelay<Int>.create(bufferSize: 3)
新的 DisposeBag
函数构建器
RxSwift 6 包含一个新的 DisposeBag
函数构建器,用于相似 SwiftUI 的无逗号语法:
var disposeBag = DisposeBag {
observable1.bind(to: input1)
observable2.drive(input2)
observable3.subscribe(onNext: { val in
print("Got (val)")
})
}
// Also works for insertions
disposeBag.insert {
observable4.subscribe()
observable5.bind(to: input5)
}
许多许多(许多)运算符重命名
咱们在 RxSwift 6 中花了一些时刻,重命名了许多运算符,以便尽或许更好地遵守 Swift 的代码攻略。
这是一个大部分完好的列表:
RxSwift 5 | RxSwift 6 |
---|---|
catchError(_:) |
catch(_:) |
catchErrorJustReturn(_:) |
catchAndReturn(_:) |
elementAt(_:) |
element(at:) |
retryWhen(_:) |
retry(when:) |
takeUntil(_:) |
take(until:) |
takeUntil(behavior:_:) |
take(until:behavior:) |
takeWhile(_:) |
take(while:) |
takeWhile(behavior:_:) |
take(while:behavior:) |
take(.seconds(3)) |
take(for: .seconds(3)) |
skipWhile(_:) |
skip(while:) |
observeOn(_:) |
observe(on:) |
subscribeOn(_:) |
subscribe(on:) |
对 XCFrameworks 更好的支撑
RxSwift 6 的每个版别现在都会绑缚一组 XCFrameworks。
因为二进制模块的稳定性,这答应轻松链接到 RxSwift 的预构建副本,而无需忧虑升级到下一个版别的 Swift 时的前向兼容性。
总结
期望您喜欢 RxSwift 6 一些最风趣的功用和更新的快速概述,但这并不是一切问题都已修正。
有很多的过错修正、改善和小弥补值得一看。请必须花点时刻检查release notes。