Advantage of Key-Value Coding in Swift 4

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.

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 of NSManagedObject
  • 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 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
}

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



Leave a reply



Submit