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.
Advantages of KVC
The example that you have used is not the optimal case of using KVC or keyPath itself.
The true power, IMO, of keyPath is unleashed when you're working with protocols. Let me give you an example -
Say you have a protocol Identifiable
which has only one member - id
. Each type that conforms to this, must use id
property which uniquely identifies it.
protocol Identifiable {
var id: Int { get }
}
struct Person: Identifiable {
var id: Int
var name: String
}
Here, a Person
type having a member id
doesn't sound too good. Consider another one -
struct Book: Identifiable {
var id: Int
var title: String
var author: String
}
Here, as well , a book can be uniquely identified by id
but it doesn't sound good.
Here's where keyPath comes into play.
You can define member inside a protocol with some name and let the conforming types write their own name for that particular protocol member. The conforming types can map or tell the compiler that their particular member is the replacement of the one inside the protocol using keyPath.
protocol Identifiable {
associatedtype ID
static var id: WritableKeyPath<Self,ID> { get }
}
struct Person: Identifiable {
static var id = \Person.socialSecurityNumber
var socialSecurityNumber: Int
var name: String
}
struct Book: Identifiable {
static var id = \Book.isbn
var isbn: String
var title: String
var author: String
}
func printID<T: Identifiable>(aType: T) {
print(aType[keyPath: T.id])
}
printID(aType: Person(socialSecurityNumber: 1234, name: "Shubham Bakshi"))
printID(aType: Book(isbn: "qwertyui", title: "The Theory of Everything", author: "Stephen W. Hawking"))
As an added advantage , your conforming type can have id
of any type , String
, Int
rather than Int
only (as was in previous case)
If you want to give only a specific type to id
, say Int
only, you can replace the definition of our protocol with this one -
protocol Identifiable {
static var id: WritableKeyPath<Self,Int> { get }
}
This will force the conforming types to use Int
for their id
substitute.
Source - Swift KeyPath - Paul Hudson
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];
In Swift, is Key-Value Coding available for custom objects without subclassing NSObject?
Unlike a formal protocol, that any object could technically conform to, NSKeyValueCoding
is available to any NSObject
via Informal Protocols:
An informal protocol is a category on NSObject, which implicitly makes
almost all objects adopters of the protocol. (A category is a language
feature that enables you to add methods to a class without subclassing
it.) Implementation of the methods in an informal protocol is
optional. Before invoking a method, the calling object checks to see
whether the target object implements it. Until optional protocol
methods were introduced in Objective-C 2.0, informal protocols were
essential to the way Foundation and AppKit classes implemented
delegation.
This is as opposed to simply implementing the KVC directly into NSObject
, I think the main benefit of the informal protocol is to split up the functionality of NSObject
into separate files. But there may be other benefits of using Informal Protocols
And because NSKeyValueCoding
is a category on NSObject
, you unfortunately cannot just make any custom object support KVC
Swift: This class is not key value coding-compliant... for Double values
Correct, you can only do key value coding for types that can be represented in Objective-C. Unfortunately, those types that are represented as primitive data types in Objective-C (e.g. Swift's Int
and Double
are represented as NSInteger
and double
in Objective-C, respectively) can not be presented as such if they are optionals. In Objective-C, optionals only make sense with class types, not fundamental data types.
CoreData is setValue(_:forKey:) necessary in Swift?
Not correct, both methods trigger KVC
.
setValue(:forKey)
is a generic method ofNSManagedObject
- Dot notation is for
NSManagedObject
subclasses accessing@NSManaged
properties. The attribute@NSManaged
enables KVC.
Both methods do the same thing.
You can even use setValue(:forKey)
in NSManagedObject
subclasses but it's recommended (and more convenient and less error-prone) to use dot notation.
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
}
What are the benefits of using tableView within a UIViewController?
1- Ability to customize the tableView height and width as you want
2- Ability to have more than 1 tableView instead of only one with UITableViewController
3- When adding a subview it's added to self.view which won't make that subview scroll with tableView like in UITableViewController
Related Topics
How to Use Swipe to Dismiss While Presenting a Fullscreen Modal in iOS 13
Control a Nstabviewcontroller from Parent View
Cannot Convert [Int] to [Int] in Generic Implementation
Horizontal Scrolling in Spritekit (Vs. a Viewcontroller)
Picker Not Working When Editmode Is Active
Make a Publisher from a Callback
How to Use Passed Parameters in Swift Setmethodcallhandler - Self.Methodname(Result: Result)
Window Title Bar Appears Transparent Issue (Not Really Transparent)
Editing a Package Dependency as a Local Package
Using Dateformatter with Timezone to Format Dates in Swift
Animation Delay on Left Side of Screen in iOS Keyboard Extension
Advantage of Key-Value Coding in Swift 4
How to Restore In-App Purchases Correctly
Swift Version Build Configuration
Passing Parameters with #Selector
Why Swift Call Too Shallow Here
Xcode Server: Opening Import File for Module 'Mobilecoreservices': Permission Denied