Swift 合并,避免嵌套订阅者可选

Swift Combine, Avoid nested subscribers on optionals

我有一个包含用户对象的 AccountService class。用户对象从网络请求中设置为异步。 在我的 UI 中,我正在显示用户并希望使更改保持最新。我正在使用 Swift Combine Framework 来执行此操作。

问题:有没有办法避免对象上的嵌套订阅者?

我写了一些测试代码来说明这一点:

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.sink 对象设置的时间 AND 对象内容更改的时间。 我在设置用户名时尝试过 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 实现中驱动所有内容。

是的,有办法。您可以简单地使用 Combine 的 flatMap 运算符:

Transforms all elements from an upstream publisher into a new publisher up to a maximum number of publishers you specify.

我的工作示例:

import Foundation
import Combine

class User: ObservableObject {
    @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?
    
    init() {
        self.userCancel = self.$user
            .filter { [=10=] != nil }
            .flatMap { [=10=]!.$name }
            .sink(receiveValue: { name in
                print(name)
            })
    }

    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"))

程序的输出将是:

Philipp
Tom
Anna