keyPathsForValuesAffecting with NSManagedObject

跟風遠走 提交于 2019-12-06 03:23:20

问题


Bonjour, I would like to translate an objective'c exercise from an Aaron's book to swift but I can't find the solution. The Objective'c code is :

@dynamic firstName;
@dynamic lastName;
@dynamic department;

+ (NSSet *)keyPathsForValuesAffectingFullName
{
    return [NSSet setWithObjects:@"firstName", @"lastName", nil];
}

- (NSString *)fullName
{
    NSString *first = [self firstName];
    NSString *last = [self lastName];
    if (!first)
        return last;
    if (!last)
        return first;
    return [NSString stringWithFormat:@"%@ %@", first, last];
}

I found a function in the developer documentation but I can't understand how to implement this code.

to be more explicit, this is the Apple doc

To-one Relationships

To trigger notifications automatically for a to-one relationship you should either override keyPathsForValuesAffectingValueForKey: or implement a suitable method that follows the pattern it defines for registering dependent keys.

For example, the full name of a person is dependent on both the first and last names. A method that returns the full name could be written as follows:

- (NSString *)fullName {
    return [NSString stringWithFormat:@"%@ %@",firstName, lastName];
}

An application observing the fullName property must be notified when either the firstName or lastName properties change, as they affect the value of the property.

One solution is to override keyPathsForValuesAffectingValueForKey: specifying that the fullName property of a person is dependent on the lastName and firstName properties. Listing 1 shows an example implementation of such a dependency:

Listing 1 Example implementation of keyPathsForValuesAffectingValueForKey:

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {

    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];

    if ([key isEqualToString:@"fullName"]) {
        NSArray *affectingKeys = @[@"lastName", @"firstName"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keyPaths;
}

class func keyPathsForValuesAffectingValueForKey(_ key: String) -> NSSet

Can somebody tell me how implement this function in swift?

Thank you for helping me.


回答1:


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




回答2:


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"])
}



回答3:


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([])
  }

}



回答4:


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.




回答5:


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)
}



回答6:


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.


来源:https://stackoverflow.com/questions/28272653/keypathsforvaluesaffecting-with-nsmanagedobject

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!