【发布时间】:2020-05-13 21:41:29
【问题描述】:
我有一个包含 User 对象的 AccountService 类。用户对象从网络请求中异步设置。 在我的 UI 中,我正在显示用户并希望使更改保持最新。我正在使用 Swift 组合框架来执行此操作。
问题:有没有办法避免对象上的嵌套订阅者?
我写了一些测试代码来说明这一点:
import UIKit
import Combine
class User: ObservableObject, CustomDebugStringConvertible {
@Published
var name: String
init(name: String) {
self.name = name
}
var debugDescription: String {
return self.name
}
}
class AccountService {
@Published
var user: User? = nil
var userCancel: AnyCancellable?
var userContentCancel: AnyCancellable?
init() {
self.userCancel = self.$user.sink { (user) in
print("Set user: \(String(describing: user))")
guard let user = user else { return }
self.userContentCancel = user.$name.sink { _ in
print("new name: \(String(describing: self.user))")
}
}
}
func setUseru(user: User) {
self.user = user
}
func changeUserName(name: String) {
self.user?.name = name
}
}
let x = AccountService()
x.setUseru(user: User(name: "Philipp"))
x.changeUserName(name: "Tom")
x.setUseru(user: User(name: "Anna"))
在 Playgroud Xcode 版本 11.4.1、Swift 5 中运行
输出
Set user: nil
Set user: Optional(Philipp)
new name: nil
new name: Optional(Philipp)
Set user: Optional(Anna)
new name: Optional(Tom)
理想情况下,我只想在设置对象时收听self.$user.sinkAND,以了解对象内容何时更改。
我在设置用户名时玩过self.objectWillChange.send(),但我无法触发外部发布者。
我正在寻找摆脱的方法
guard let user = user else { return }
self.userContentCancel = user.$name.sink { _ in
print("new name: \(String(describing: self.user))")
}
在我的实现中,只需从同一个 self.$user.sink { (user) in 实现中驱动所有内容。
【问题讨论】:
-
如果可能的话,你的用户应该是一个结构体,因此是不可变的。然后您将只拥有外部订阅者,因为更改名称需要
User的新实例。如果需要,请使用相同的底层唯一标识符确认Identifiable,以便您知道这是具有不同名称的同一用户。您不能从User中触发AccountService发布者,因为用户对象不知道它被帐户服务引用 -
@Paulw11 这是个好主意。我会到处玩。问题是可以通过监视用户对象的更改侦听器来更新用户对象。这是在用户内部实现的,因此它不会污染其余代码。所以
changeUserName()在更新时从内部调用。 -
如果你采用不变性,那么
changeUserName()就会消失。你可以在你的结构user(withNewName:String)上有一个函数,它返回一个新的用户实例,它的其他属性相同,但名称是新的。很难确切地说出你应该做什么,因为我不知道你想要达到什么目标。您可以使用combineLatest将两个发布者合并为一个。 -
将它们组合成一个似乎是个好主意。我也可以很好地隐藏它。感谢@Paulw11 的想法。一旦我找到一个优雅的解决方案,我会在这里更新它