How to Use Key-Value Coding in Swfit 4.0

How do I use Key-Value Coding in Swfit 4.0?

To implement KVC support for a property in Swift 4, you need two things:

  1. Since the current implementation of KVC is written in Objective-C, you need the @objc annotation on your property so that Objective-C can see it. This also means that the property's type needs to be compatible with Objective-C.

  2. In addition to exposing the property to Objective-C, you will need to set up your notifications in order for observers to be notified when the property changes. There are three ways to do this:

For stored properties, the easiest thing to do is to add the dynamic keyword like so:

@objc dynamic var foo: String

This will allow Cocoa to use Objective-C magic to automagically generate the needed notifications for you, and is usually what you want. However, if you need finer control, you can also write the notification code manually:

@objc private static let automaticallyNotifiesObserversOfFoo = false
@objc var foo: String {
willSet { self.willChangeValue(for: \.foo) }
didSet { self.didChangeValue(for: \.foo) }
}

The automaticallyNotifiesObserversOf<property name> property is there to signify to the KVC/KVO system that we are handling the notifications ourselves and that Cocoa shouldn't try to generate them for us.

Finally, if your property is not stored, but rather depends on some other property or properties, you need to implement a keyPathsForValuesAffecting<your property name here> method like so:

@objc dynamic var foo: Int
@objc dynamic var bar: Int

@objc private static let keyPathsForValuesAffectingBaz: Set<String> = [
#keyPath(foo), #keyPath(bar)
]
@objc var baz: Int { return self.foo + self.bar }

In the example above, an observer of the baz property will be notified when the value for foo or the value for bar changes.

Advantage of Key-Value Coding in Swift 4

Two key advantages are:

  1. Keys are validated: Xcode will warn you, at compile time, if you try to use a key that is not valid.

  2. Results are strongly typed: With the following, the compiler will know that the resulting name is a String:

    let name = student[keyPath: nameKey]

    This eliminates the need to do any casting.

The result is that it's much easier to write safe code.

What is Key-Value-Coding and Key-Value-Observing in Objective C?

Key-Value-Coding (KVC) means accessing a property or value using a string.

id someValue = [myObject valueForKeyPath:@"foo.bar.baz"];

Which could be the same as:

id someValue = [[[myObject foo] bar] baz];

Key-Value-Observing (KVO) allows you to observe changes to a property or value.

To observe a property using KVO you would identify to property with a string; i.e., using KVC. Therefore, the observable object must be KVC compliant.

[myObject addObserver:self forKeyPath:@"foo.bar.baz" options:0 context:NULL];

Key-Value Observing in Swift 4

I think the reason change is coming up with nil is because you haven't specified options.

Rewrite as follows:

override func viewDidLoad() {
super.viewDidLoad()

// configure the observation
token = self.observe(\.dishes, options: [.new,.old]) { object, change in

print(object)
let set1 = Set(change.newArray!)
let set2 = Set(change.oldArray!)

let filter = Array(set1.subtract(set2))
print(filter)

}

updateTableView()
}

Note that I have done a bit of guesswork here about your Dish object. I am assuming you have made it conform to the Equatable protocol, and this is a necessary step for the solution to work.

UPDATE: This requirement has now been reflected in the official Apple documentation here.

If you don't need to know how a property has changed, omit the options parameter. Omitting the options parameter forgoes storing the new and old property values, which causes the oldValue and newValue properties to be nil.

CoreData: this class is not key value coding-compliant for the key folderPosition

First of all and unrelated to the issue the logic to reindex the position looks pretty weird. Actually you have to decrement the index only from the item at position deleted position + 1.

As mentioned in the comments the error is caused by a typo. You mean

items.setValue(items.folderPosition - 1, forKey: "folderPosition")

instead of

managedObjectContext.setValue(items.folderPosition - 1, forKey: "folderPosition")

There are many bad practices in the code.

  • The naming is confusing. Name entities and loop elements in singular form: Folder and for item in ....
  • Avoid redundant information. Rename folderPosition to position
  • Prefer dot notation over KVC.
  • Take advantage of the generic fetch request: NSFetchRequest<Folders>.
  • Never check for an empty string or empty array with count == 0. There is isEmpty.
  • The empty check is redundant anyway. The loop is being skipped if the array is empty.
  • Never print meaningless literal strings in catch blocks. Print the error.


static func updateFolderPositionsAfterFolderDeletion(managedObjectContext: NSManagedObjectContext = AppDelegate.viewContext) {

let fetchRequest = NSFetchRequest<Folders>(entityName: "Folders")
let sortDescriptor = NSSortDescriptor(key: "folderPosition", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
do {
let folders = try managedObjectContext.fetch(fetchRequest)
for folder in folders {
folder.folderPosition = folder.folderPosition - 1
}
try managedObjectContext.save()
}
catch {
print(error)
}
}

To reindex the entire data set starting at 0 there is a smarter way:

for (index, folder) in folders.enumerated() {
folder.folderPosition = index
}


Related Topics



Leave a reply



Submit