В императивном Swift обычно используют вычисляемые свойства, чтобы обеспечить удобный доступ к данным без дублирования состояния.
Допустим, у меня есть этот класс для императивного использования MVC:
class ImperativeUserManager {
private(set) var currentUser: User? {
didSet {
if oldValue != currentUser {
NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
// Observers that receive this notification might then check either currentUser or userIsLoggedIn for the latest state
}
}
}
var userIsLoggedIn: Bool {
currentUser != nil
}
// ...
}
Если я хочу создать реактивный эквивалент в Combine, например, для использования с SwiftUI, я могу легко добавить @Published
сохраненные свойства для генерации Publisher
s, но не для вычисляемых свойств.
@Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
currentUser != nil
}
Есть различные обходные пути, о которых я мог подумать. Вместо этого я могу сохранить вычисленное свойство и обновлять его.
Вариант 1. Использование наблюдателя свойства:
class ReactiveUserManager1: ObservableObject {
@Published private(set) var currentUser: User? {
didSet {
userIsLoggedIn = currentUser != nil
}
}
@Published private(set) var userIsLoggedIn: Bool = false
// ...
}
Вариант 2: Использование Subscriber
в моем собственном классе:
class ReactiveUserManager2: ObservableObject {
@Published private(set) var currentUser: User?
@Published private(set) var userIsLoggedIn: Bool = false
private var subscribers = Set<AnyCancellable>()
init() {
$currentUser
.map { $0 != nil }
.assign(to: \.userIsLoggedIn, on: self)
.store(in: &subscribers)
}
// ...
}
Однако эти обходные пути не так элегантны, как вычисляемые свойства. Они дублируют состояние и не обновляют оба свойства одновременно.
Что будет правильным эквивалентом добавления Publisher
к вычисляемому свойству в Combine?
ObservableObject
. Вы по своей сути считать , чтоObservableObject
объект должен иметь возможность иметь мутирует способность , которая, по определению, не является случаем для компьютерной собственности .Ответы:
Как насчет использования downstream?
Таким образом, подписка получит элемент из апстрима, затем вы можете использовать
sink
илиassign
сделатьdidSet
идею.источник
Создайте нового издателя, подписавшегося на свойство, которое вы хотите отслеживать.
Затем вы сможете наблюдать это так же, как ваша
@Published
собственность.Прямо не связано, но полезно, тем не менее, вы можете отслеживать несколько свойств таким образом
combineLatest
.источник
Вы должны объявить объект PassthroughSubject в вашем ObservableObject:
И в didSet (willSet может быть лучше) вашего @Published var вы будете использовать метод send ()
Вы можете проверить это в WWDC Data Flow Talk.
источник
@Published
Обертка иPassthroughSubject
оба служат той же цели в этом контексте. Обратите внимание на то, что вы написали и чего на самом деле хотел достичь ОП. Является ли ваше решение лучшей альтернативой варианту 1 ?scan ( : :) Преобразует элементы из вышестоящего издателя, предоставляя текущий элемент замыканию вместе с последним значением, возвращаемым замыканием.
Вы можете использовать scan (), чтобы получить последнее и текущее значение. Пример:
Выше код эквивалентен этому: (меньше объединить)
источник