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.
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
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:
- Add the
dynamic
keyword to any property you want to observe. - Create a global context variable as per Apple's documentation.
- Override the
observeValueForKeyPath
method.
来源:https://stackoverflow.com/questions/28272653/keypathsforvaluesaffecting-with-nsmanagedobject