I have been trying to figure this out for a while but cannot understand the advantage of KVC other than :
I am not sure if there is any advantage of using KVC other than the 2 cases said above (i know i might be wrong) but i could not find one !
Like consider the following code :
class Profile: NSObject {
@objc var firstName: String
var lastName: String
init(firstName: String,lastName: String) {
self.firstName = firstName
self.lastName = lastName
super.init()
}
}
let profile1 = Profile(firstName: "John", lastName: "Doe")
profile1.firstName // returns String "John"
profile1.value(forKey: "firstName") // returns Optional<Any>
let firstNameKey = \Profile.firstName
profile1[keyPath: firstNameKey] /* returns String "John" */
I mean why would i use :
let firstNameKey = \Profile.firstName
profile1[keyPath: firstNameKey] /* returns String "John" */
instead of :
profile1.firstName // returns String "John"
And if someone has some code sample/examples, then if they can explain it using swift , it would be great (as my Objective-C is not good)
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