Оболочка @Published свойства не работает на подклассе ObservableObject
У меня есть класс, соответствующий протоколу @ObservableObject, и я создал из него подкласс со своей собственной переменной с помощью оболочки свойств @Published для управления состоянием.
Кажется, что обертка свойства @published игнорируется при использовании подкласса. Кто-нибудь знает, если это ожидаемое поведение и есть ли обходной путь?
Я использую iOS 13 Beta 8 и xCode Beta 6.
Вот пример того, что я вижу. При обновлении TextField на MyTestObject
текстовое представление корректно обновляется значением aString. Если я обновлю MyInheritedObject
TextField значение anotherString не обновляется в текстовом представлении.
import SwiftUI
class MyTestObject: ObservableObject {
@Published var aString: String = ""
}
class MyInheritedObject: MyTestObject {
@Published var anotherString: String = ""
}
struct TestObserverWithSheet: View {
@ObservedObject var myTestObject = MyInheritedObject()
@ObservedObject var myInheritedObject = MyInheritedObject()
var body: some View {
NavigationView {
VStack(alignment: .leading) {
TextField("Update aString", text: self.$myTestObject.aString)
Text("Value of aString is: \(self.myTestObject.aString)")
TextField("Update anotherString", text: self.$myInheritedObject.anotherString)
Text("Value of anotherString is: \(self.myInheritedObject.anotherString)")
}
}
}
}
6 ответов
Наконец-то разобрался с решением / обходом этой проблемы. Если вы удалите оболочку свойства из подкласса и вызовете базовый класс objectWillChange.send() для переменной, состояние будет обновлено должным образом.
ПРИМЕЧАНИЕ: не переопределять let objectWillChange = PassthroughSubject<Void, Never>()
на подклассе, поскольку это снова приведет к тому, что состояние не будет обновляться должным образом.
Я надеюсь, что это будет исправлено в будущих версиях, так как objectWillChange.send()
много шаблонного для поддержания.
Вот полностью рабочий пример:
import SwiftUI
class MyTestObject: ObservableObject {
@Published var aString: String = ""
}
class MyInheritedObject: MyTestObject {
// Using @Published doesn't work on a subclass
// @Published var anotherString: String = ""
// If you add the following to the subclass updating the state also doesn't work properly
// let objectWillChange = PassthroughSubject<Void, Never>()
// But if you update the value you want to maintain state
// of using the objectWillChange.send() method provided by the
// baseclass the state gets updated properly... Jaayy!
var anotherString: String = "" {
willSet { self.objectWillChange.send() }
}
}
struct MyTestView: View {
@ObservedObject var myTestObject = MyTestObject()
@ObservedObject var myInheritedObject = MyInheritedObject()
var body: some View {
NavigationView {
VStack(alignment: .leading) {
TextField("Update aString", text: self.$myTestObject.aString)
Text("Value of aString is: \(self.myTestObject.aString)")
TextField("Update anotherString", text: self.$myInheritedObject.anotherString)
Text("Value of anotherString is: \(self.myInheritedObject.anotherString)")
}
}
}
}
iOS 14.5 решает эту проблему.
Комбинировать
Решенные вопросы
Использование опубликованного в подклассе типа, соответствующего ObservableObject, теперь правильно публикует изменения. (71816443)
Это связано с тем, что ObservableObject - это протокол, поэтому ваш подкласс должен соответствовать протоколу, а не вашему родительскому классу.
Пример:
class MyTestObject {
@Published var aString: String = ""
}
final class MyInheritedObject: MyTestObject, ObservableObject {
@Published var anotherString: String = ""
}
Теперь свойства @Published как для класса, так и для подкласса будут запускать события просмотра
Лучшее решение этой проблемы, которое я нашел, заключается в следующем:
Объявить издателю:
open class BaseObservableObject: ObservableObject {
public let objectWillChange = ObservableObjectPublisher()
}
Затем, чтобы вызвать
objectWillChange
в своем подклассе вы должны обрабатывать изменения как наблюдаемых классов, так и типов значений:
class MyState: BaseObservableObject {
var classVar = SomeObservableClass()
var typeVar: Bool = false {
willSet { objectWillChange.send() }
}
var someOtherTypeVar: String = "no observation for this"
var cancellables = Set<AnyCancellable>()
init() {
classVar.objectWillChange // manual observation necessary
.sink(receiveValue: { [weak self] _ in
self?.objectWillChange.send()
})
.store(in: &cancellables)
}
}
А затем вы можете продолжить создание подклассов и добавить наблюдение, где это необходимо:
class SubState: MyState {
var subVar: Bool = false {
willSet { objectWillChange.send() }
}
}
Вы можете пропустить наследование
BaseObservableObject
в корневом родительском классе, если этот класс уже содержит переменные, поскольку затем синтезируется издатель. Но будьте осторожны, если уберете финальную
@Published
значение из корневого родительского класса, все
objectWillChange.send()
в подклассах молча перестанут работать.
Очень жаль, что приходится проходить эти шаги, потому что очень легко забыть добавить наблюдение, если вы добавите переменную в будущем. Надеюсь, мы получим лучшее официальное исправление.
Это также происходит, когда ваш класс не является напрямую подклассом ObservableObject
:
class YourModel: NSObject, ObservableObject {
@Published var value = false {
willSet {
self.objectWillChange.send()
}
}
}
По моему опыту, просто соедините подкласс
objectWillChange
с базовым классом
objectWillChange
как это:
class GenericViewModel: ObservableObject {
}
class ViewModel: GenericViewModel {
@Published var ...
private var cancellableSet = Set<AnyCancellable>()
override init() {
super.init()
objectWillChange
.sink { super.objectWillChange.send() }
.store(in: &cancellableSet)
}
}