Есть ли способ получить доступ к включающему экземпляру ObservableObject для вызова objectWillChange.send() из любого места оболочки свойства?
Я пытаюсь создать обертку свойств, похожую наCombine
'sPublished
one(для нужд моего проекта), но с возможностью изменять обернутое свойство, отправляя значение издателю, хранящееся вprojectedValue
, так:
// in class
@PublishedMutable var foo = "foo"
$foo.send("bar")
// ...
Вот код оболочки свойства:
@propertyWrapper
struct PublishedMutable<Value> {
static subscript<T: ObservableObject>(
_enclosingInstance instance: T,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<T, Value>,
storage storageKeyPath: ReferenceWritableKeyPath<T, Self>
) -> Value {
get {
instance[keyPath: storageKeyPath].storage
}
set {
let publisher = instance.objectWillChange
// This assumption is definitely not safe to make in
// production code, but it's fine for this demo purpose:
(publisher as? ObservableObjectPublisher)?.send()
instance[keyPath: storageKeyPath].storage = newValue
}
}
@available(*, unavailable,
message: "@PublishedMutable can only be applied to classes"
)
var wrappedValue: Value {
get { fatalError() }
set { fatalError() }
}
private var storage: Value{
get { publisher.value }
set { publisher.send(newValue) }
}
typealias Publisher = CurrentValueSubject<Value, Never>
var projectedValue: Publisher { self.publisher }
private var publisher: Publisher
init(initialValue: Value) {
self.publisher = Publisher(initialValue)
}
init(wrappedValue: Value) {
self.init(initialValue: wrappedValue)
}
}
А вот код игровой площадки, который я использовал для тестирования ее функций:
class AmogusComic: ObservableObject {
@PublishedMutable var currentPageContent = "S O S"
private var cancellables: Set<AnyCancellable> = []
func connect() {
$currentPageContent.sink { newPage in
print(newPage)
}
.store(in: &cancellables)
objectWillChange.sink { _ in
print("Update")
}
.store(in: &cancellables)
}
}
let amogusComic = AmogusComic()
DispatchQueue.main.async {
// connect publishers and print initial content. prints "S O S"
amogusComic.connect()
// change the value of current page via setting a property, the amogusComic class will reactively print "S U S" after
amogusComic.currentPageContent = "S U S"
// take a property publisher and send new value to publisher, the amogusComic class will reactively print "A M O G U S" after
amogusComic.$currentPageContent.send("A M O G U S")
}
PlaygroundPage.current.needsIndefiniteExecution = true
Простое изменение значения путем непосредственного изменения свойства и отправки значения издателю работает отлично. Но я также хочу позвонитьobjectWillChange.send()
каждый раз, когда значение меняется. Проблема в том, что оно вызывается только при непосредственном изменении обернутого свойства, что является неправильным поведением.
Тестовый код выводит это
S O S
Update
S U S
A M O G U S
когда я думаю, что это должно вывести это
S O S
Update
S U S
Update
A M O G U S
Как вы можете видеть в приведенном выше коде, я использовал статический индекс для получения и вызова, но оказывается, что Swift вызывает этот индекс только при непосредственном изменении свойства (amogusComic.currentPageContent = "S U S"
)
ПолучающийMirror(reflecting: self).superclassMirror
тоже не работает,superclassMirror
всегда возвращает ноль
Могу ли я как-нибудь получитьObservableObject
вне_enclosingInstance
статический индекс? Чтобы я мог позвонитьobjectWillChange
также при отправке значения издателю
1 ответ
Нет. У нас нет доступа к статическому индексному эквиваленту . Это секрет Apple: как подписывается на родительский объектobjectWillChange
, без необходимости сначала вызывать его геттер или сеттер.
Вы можете только воссоздать (и вы это сделали)Published
'sprojectedValue
если класс, с которым вы его используете, не является
ObservableObject
.
Тем не менее, в этом нет необходимости, если вы намерены простоsend
метод.
final class Object: ObservableObject {
@Published var published = ""
}
let object = Object()
object.objectWillChange.sink { print("objectWillChange") }
object.$published.send("new value")
// "objectWillChange"
object.published // "new value"
public extension Published.Publisher {
mutating func send(_ value: Value) {
Just(value).assign(to: &self)
}
}