keyPathsForValuesAffecting with NSManagedObject

旧城冷巷雨未停 提交于 2019-12-04 07:24:24

I found the solution of my problem! Just override the func keyPathsForValuesAffectingValueForKey(key: String) with class before

Here the code:

class Locataires: NSManagedObject {
@NSManaged var firstName: String
@NSManaged var lastName: String
var fullName: NSString {
    get {
 return firstName + lastName
    }
}

override class func keyPathsForValuesAffectingValueForKey(key: String) -> NSSet {
    if key == «fullName « {
        let mesClefs = ["firstName", "lastName"]
        return NSSet(array: mesClefs)
    }
    else {
        return super.keyPathsForValuesAffectingValueForKey(key)
    }
}

Thanks for the help Jan

avvensis

For Swift 4 solution is:

@objc dynamic var firstName: String = ""
@objc dynamic var lastName: String = ""
@objc dynamic var fullName: String {
  return "\(firstName) \(lastName)"
}

@objc class func keyPathsForValuesAffectingFullName() -> Set<String> {
  return Set(["firstName", "lastName"])
}

This solution works for XCode 9+, and Swift 4.0 (Apple Swift version 4.0.2 (swiftlang-900.0.69.2 clang-900.0.38):

@objc public class MyKVOClass : NSObject
{
    // MARK: Properties

    @objc dynamic var myKVOSubclass : KVOSubclass?
    @objc dynamic var myKVOProperty : String?

    @objc dynamic var myComputedKVOProperty : String? {
        guard let mySubclassPropertyValue = self.myKVOSubclass?.mySubclassProperty,
            let myKVOPropertyValue = self.myKVOProperty else { return nil }

        let myComputedKVOPropertyValue = NSString(format:"%@ %@",mySubclassPropertyValue, myKVOPropertyValue)
        return myComputedKVOPropertyValue as String
    }

    // MARK: Initialization

    @objc public required override init() {
        super.init()
    }

  // MARK: KVO

  public override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String>
  {
    if key == "myComputedKVOProperty" {
      return Set(["myKVOSubclass",
      "myKVOSubclass.mySubclassProperty",
      "myKVOProperty"])
    }
    // NOTE : Add more keys with the name of the property if needed...

    return Set([])
  }

}

Thank you Darkwonder. It works in Swift 4!!!

Before - Adding didSet to all dependent properties:

class MyViewController: NSViewController {

  // bound to a Value of NSTextField with NumberFormatter
  @objc dynamic var amountOfBetOnRed: Int = 0 {
    didSet {
      updatebetButtonEnabled()
    }
  }

  // same above
  @objc dynamic var amountOfBetOnBlack: Int = 0 {
    didSet {
      updatebetButtonEnabled()
    }
  }

  // bound to a Enabled of NSButton
  @objc dynamic var betButtonEnabled: Bool = false

  func updatebetButtonEnabled() {
    betButtonEnabled = amountOfBetOnRed != 0 || amountOfBetOnBlack != 0
  }

}

After - Replacing the didSet(s) with a computed property:

class MyViewController: NSViewController {

  @objc dynamic var amountOfBetOnRed: Int = 0

  @objc dynamic var amountOfBetOnBlack: Int = 0

  @objc dynamic var betButtonEnabled: Bool {
    get {
      return amountOfBetOnRed != 0 || amountOfBetOnBlack != 0
    }
  }

  override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
    print("Debug: called for:", key)

    switch key {
    case "betButtonEnabled" :
      return Set(["amountOfBetOnRed", "amountOfBetOnBlack"])
    default :
      return super.keyPathsForValuesAffectingValue(forKey: key)
    }
  }

}

get { } can be omitted and just leave return ..., but I used get here to highlight that it is a computed property.

Confirmed with Xcode 9.0 with a pseudo code like above.

Added:

What I have learned so far: The function keyPathsForValuesAffectingValue is called several times with each property name specified as @objc dynamic one by one after returning from init(). This implementation allows us to tell what properties are depended on what properties. Actually, addObserver is automatically called on behalf of us behind the scenes. That function is not called for other ordinal properties such as just var without @objc dynamic.

Here's a safer (because of #keyPath), shorter version for Swift 4:

override public class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
    return [
        #keyPath(Track.rTitle): [#keyPath(Track.title)],
        #keyPath(Track.rSource): [#keyPath(Track.album), #keyPath(Track.author)],
        ][key] ?? super.keyPathsForValuesAffectingValue(forKey: key)
}

If you just want to check when the names or department is updated, just use Swift's dynamic keyword like so:

dynamic var firstName: String

You can then follow Apple's documentation on adopting Key-Value observing in Swift by following the Adopting Cocoa Design Patterns Guide

Summary:

  1. Add the dynamic keyword to any property you want to observe.
  2. Create a global context variable as per Apple's documentation.
  3. Override the observeValueForKeyPath method.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!