RxSwift 6 更新了什么

原文:What's new in RxSwift 6 ?

新的 2021 年快乐!这是相当艰难的一年,我相信我们都希望有更好的一年,我们终于能够过上正常的生活。

新年伊始,还有什么比发布新版本更好的事情呢?向 RxSwift 6 问好

这篇文章将向你快速概述一些可能影响你的最值得注意的变化。

注意:这只是一些有趣更新的部分列表,显然不包括大量较小的错误修复和改进。

有关完整的更改日志,请查看 release notes

话不多说,让我们直接开始吧!

当然,这不是技术上的改变,但绝对值得一提。

RxSwift 始终使用 Reactive Extensions 的原始 Volta Eel 徽标,但我觉得这个主要版本可能是一个很好的机会,可以为 RxSwift 的徽标添加一些独特的优势。

这是一个以其自己的精神和身份使其独一无二的机会,同时仍然尊重原始的 ReactiveX 徽标以及 Swift 的徽标。

我给你,新的 RxSwift Logo!

Binder 从 RxCocoa 迁移到 RxSwift

这是一个微小但强烈要求的改变,而且是有意义的。 Binder,顾名思义,允许你定义一种将 Observable 可观察序列绑定到其中的方法,以反应性地提供绑定的输入。

例如:

swift 复制代码
viewModel.isButtonEnable.bind(to: myButton.rx.isEnabled)

使用底层 Binder 让你绑定到 rx.isEnabled

Binder 始终存在于 RxCocoa 内部,但我们社区的用例和各种讨论表明,它是一个超级有用的实体,可以为更广泛的 RxSwift 受众提供服务,因此它现在是其中的一部分,并且 RxCocoa 不需要使用 Binder。

使用 @dynamicMemberLookup 自动建立 Binders

RxSwift 包含一个名为 .rx 的命名空间,它允许你为特定对象放置自己的反应式扩展。

例如,给定一个如下所示的自定义 MyView

swift 复制代码
class MyView: UIView { 
    var title: String
    var subtitle: String?
    var icon: UIImage?
}

创建反应式绑定的常见模式通常如下所示:

swift 复制代码
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
       }
    }
}

这将允许你将适当类型的可观察序列绑定到各种反应性输入:

swift 复制代码
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 的弱引用,以便您可以将发出的值传递给所有者,例如:

swift 复制代码
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 开始,您可以像这样重写上面的代码:

swift 复制代码
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 的对象:

swift 复制代码
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,您可能会想 --- 嘿,DriverSignal 之间有什么区别?

首先,Infallible 位于 RxSwift 中,而另外两个位于 RxCocoa 中。但更重要的是,DriverSignal 始终使用 MainScheduler 并共享资源(使用 share())。 Infallible 的情况并非如此,它完全是一个基础的可观察序列,仅具有编译时保证无误性的功能。

Observable<Data> 的新解码 (type:decoder:) 运算符

RxSwift 6 添加了一个解码运算符,专门用于发出数据的 Observables,类似于合并:

swift 复制代码
service.rx
       .fetchJSONUsers() // Observable<Data>
       .decode(type: [User].self, decoder: JSONDecoder()) // Observable<[User]>

可变参数 drive()emit()

RxSwift 5 引入了可变参数绑定,它可以让您执行以下操作:

swift 复制代码
viewModel.string.bind(to: input1, input2, input3)

RxSwift 6 现在为 DriverSignal 带来了相同的可变参数绑定 - 使用可变参数驱动和发出操作符:

swift 复制代码
viewModel.string.drive(input1, input2, input3)
viewModel.number.emit(input4, input5)

Single 现在更好地遵循 Swift 的 Result

直到 RxSwift 5,Single 有一个自定义事件:

swift 复制代码
public enum SingleEvent<Element> {
    case success(Element)
    case error(Swift.Error)
}

如果您看到这个并说,嘿 - 这看起来很像 Result<Element, Swift.Error>,那么你是对的!

从 RxSwift 6 开始,SingleEvent 只是 Result<Element, Swift.Error> 的别名。

这一变化也体现在其他 API 中,例如 subscribe

swift 复制代码
// 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 是一个超级有用的运算符,它可以让您删除相同的值排放以避免浪费地处理它们。

例如:

swift 复制代码
myStream.distinctUntilChanged { $0.searchTerm == $1.searchTerm }

这是关键路径非常有用的另一种情况。从 RxSwift 6 开始,您可以简单地编写:

swift 复制代码
myStream.distinctUntilChanged(at: \.searchTerm)

新的 ReplayRelay

Relay 围绕 Subject,让您以仅处理值的方式中继消息,因为中继保证永远不会失败或完成。

ReplayRelay 是 RxSwift 6 的最新补充,除了现有的 BehaviorRelayPublishRelay 之外,它还包装了 ReplaySubject

创建一个使用与创建 ReplaySubject 完全相同的接口:

swift 复制代码
// Subject
ReplaySubject<Int>.create(bufferSize: 3)

// Relay
ReplayRelay<Int>.create(bufferSize: 3)

新的 DisposeBag 函数构建器

RxSwift 6 包含一个新的 DisposeBag 函数构建器,用于类似 SwiftUI 的无逗号语法:

swift 复制代码
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

相关推荐
Swift社区9 小时前
Apple 新品发布会亮点有哪些 | Swift 周报 issue 61
ios·swiftui·swift
营赢盈英3 天前
OpenAI GPT-3 API error: “You must provide a model parameter“
chatgpt·gpt-3·openai·swift
一只不会编程的猫3 天前
高德地图绘图,点标记,并计算中心点
开发语言·ios·swift
loongloongz3 天前
Swift语言基础教程、Swift练手小项目、Swift知识点实例化学习
开发语言·学习·swift
2401_858120537 天前
深入理解 Swift 中的隐式解包可选类型(Implicitly Unwrapped Optionals)
开发语言·ios·swift
quaer7 天前
QT chart案例
开发语言·qt·swift
安和昂8 天前
【iOS】UIViewController的生命周期
ios·xcode·swift
00圈圈8 天前
Swift 创建扩展(Extension)
ios·swift·extension
2401_858120538 天前
Swift 中的函数:定义、使用与实践指南
开发语言·ssh·swift
quaer9 天前
VS+QT--实现二进制和十进制的转换(含分数部分)
java·开发语言·python·qt·swift