Основная проблема заключается в том, что KVO создается с использованием Objective-C и использует среду выполнения Objective-C для обнаружения существования keyPathsForValuesAffecting
методов. В Swift 4 методы больше не отображаются в Objective-C по умолчанию, если в них не добавлена аннотация @objc
. Итак, в двух словах, добавление аннотации @objc
, вероятно, решит вашу проблему.
Еще одна вещь, которую я делаю - не является строго необходимой, но она делает код более привлекательным - это объявлять их как статические константы. @objc
заставит их подвергаться Objective-C как методам класса, так что все это работает, и это немного чище. Мне также нравится помещать на них private
, поскольку они никогда не будут вызываться кодом Swift, и нет смысла загромождать внутренний и / или публичный интерфейс вашего класса.
Вам также необходимо убедиться, что ваше свойство state
соответствует KVO, и отправлять уведомления при его изменении. Вы можете сделать это, задав свойство dynamic
, которое заставит систему KVO автоматически генерировать для вас вызовы уведомлений, или вы можете вручную вызвать willChangeValue(for:)
и didChangeValue(for:)
(или строковые версии * 1014). * и didChangeValue(forKey:)
) в ваших обработчиках willSet
и didSet
для свойства.
Наконец, не используйте необработанные строковые ключи в Swift, если вы можете избежать этого. Механизм #keyPath()
является предпочтительным способом получения основанных на строках путей к ключам (и для использования, отличного от этих устаревших методов Objective-C, которые должны принимать строки, вы должны использовать новый тип KeyPath
, который еще лучше). Если ваше свойство state
не является Objective-C-совместимым типом, вы застряли со старыми путями строковых ключей (в этом случае вы будете запускать уведомления в ваших willSet
и didSet
, как описано в предыдущий абзац). Кроме того, вы можете создать фиктивный объект Any
, который отражает ваше свойство state
, исключительно для целей KVO.
Итак, как-то так:
@objc private static let keyPathsForValuesAffectingIsReady: Set<String> = [
#keyPath(state)
]
Теперь свойство state
. Если это Objective-C-совместимый тип, это просто:
@objc dynamic var state: ...
Или, если это не так:
@objc var state: SomeNonObjCThing {
willSet { self.willChangeValue(forKey: "state") }
didSet { self.didChangeValue(forKey: "state") }
}
OR
@objc private var _stateKVO: Any { return self.state }
var state: SomeNonObjCThing {
willSet { self.willChangeValue(for: \.stateKVO) }
didSet { self.didChangeValue(for: \.stateKVO) }
}
// you can now use #keyPath(_stateKVO) in keyPathsForValuesAffecting...