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:
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.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:
Keys are validated: Xcode will warn you, at compile time, if you try to use a key that is not valid.
Results are strongly typed: With the following, the compiler will know that the resulting
name
is aString
: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
andfor item in ...
. - Avoid redundant information. Rename
folderPosition
toposition
- 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 isisEmpty
. - 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 theerror
.
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
Swift Uinavigation Bottom Line and Shadow Remove Without Navbar Color Change
How to Make a Popup Window with an Image Swift
Icloud Document Picker from Wkwebview Dismissing Container View
How to Make a Bullet List with Swift
Swift Get Specific Value from Firebase Database
iOS - Send Image to Instagram - Documentinteraction
How to Get Multiple Buttons from a Single Tableviewcell
Endless Scrolling (Repeating) Background in Spritekit Game - Swift
How to Enable Only Numeric Digit Keyboard iOS Swift
Gmsmarker Icon in the Top Left Corner of the View (Ios)
Save Depth Images from Truedepth Camera
Spritekit/Swift - How to Check Contact of Two Nodes When They Are Already in Contact
Creating Tableview Sections from JSON Data Swift 4
How to Use Realm.Addnotificationblock