This workshop will be retired on May 31, 2020.
Bummer! This is just a preview. You need to be signed in with a Basic account to view the entire video.
Key Paths13:08 with Pasan Premaratne
Swift 4 introduces all new type safe key paths and in this video we take a look at the syntax and usage
The next big change in Swift 4 is keyPaths. 0:00 Key paths are an important part of Coco development, and Swift 3 introduced string 0:03 keyPaths to the language that at compile time verified the Objective C key path. 0:09 So let's type some code. 0:14 We'll define a class named User. 0:16 This will inherit from NSObject. 0:18 And we'll give it a few properties. 0:22 This is a class so I need an init method. 0:30 So initialize with a string and a username. 0:34 Self.name = name self.username = username. 0:40 Okay, so here's a simple example of what it would look like in Swift 3. 0:44 We have a NSObject subclass with two properties. 0:49 Now we can expose individual properties to the Objective-C runtime, 0:52 by adding the at @objc keyword, or by declaring these properties as dynamic. 0:57 We'll ignore the nuances between those two approaches. 1:03 Now that we've defined this, we can define a keyPath on these observable properties. 1:07 So we could say, let keyPath = #keyPath User.name. 1:13 So again, this is the Swift 3 approach. 1:20 With this keyPath we can now observe properties using KVO and read and 1:23 write to these properties using KVC. 1:27 So first let's define an instance, so User, 1:30 we'll give it a name and a username. 1:34 We have a keyPath that we defined earlier, 1:40 so we can grab the name here as a user.vallueforKey: keyPath. 1:43 And then similarly we can set the value so we'll set this to 1:48 my full name again, for the key defined by the keyPath. 1:53 So while this was all well and good there are several downsides to this approach. 1:58 Ultimately this keyPath results to a simple string, 2:03 which you can see here in the results area as well, just says name. 2:07 There is no type safety here and 2:11 as a result we can run into simple errors that really shouldn't happen in Swift. 2:13 Name is a property of type string up here. 2:19 But because there's no type checking down over here in user.setValue, 2:24 I can pass in an integer, or a double, or any other value here, and 2:29 we wouldn't get a compiler error. 2:34 Any errors that come up are caused at runtime, and 2:36 since the playground is in always-execute mode we get an error here, but 2:40 in a normal project nothing would come up at compile time. 2:44 Now in addition, this only applies to Objective C classes, that is, 2:48 subclasses of NSObject. 2:52 We wouldn't be able to define keypads on Swift value types. 2:54 Swift 4 changes all of this with strongly typed keyPaths that work across platforms, 2:58 so Linux as well, and they apply to value types too. 3:05 Let's see what kind of changes we are looking at syntax wise. 3:09 Now instead of modifying existing code, let's add it below so we can compare. 3:12 So right here, we'll create a new keyPath. 3:17 To create a Swift 4 keyPath, we start with a backslash followed by the type and 3:20 the property we are trying to access. 3:25 So let nameKeyPath = so we start with a backlash, 3:27 the type, and the property we're trying to access. 3:33 So here, we have a keyPath defined in Swift 4. 3:39 If you option click on the keyPath constant, 3:43 the nameKeyPath constant you'll see that the type, over here rather. 3:46 So if you option click on the first one we defined, this one is a string. 3:52 In contrast, nameKeyPath is a generic type 3:58 ReferenceWritableKeyPath or a keyPath object. 4:02 And it contains information about the class we're referring to User and 4:06 the type of the underlying property, string. 4:12 Since this keyPath is to a property on a class, or a reference type, 4:15 the type of this keyPath is ReferenceWritable. 4:19 Meaning that we have information about the value semantics of this type. 4:23 So how do we use this? 4:27 Earlier we used user.value and setValue but here, the syntax is different. 4:29 The syntax looks sort of like a subscript, 4:35 except it really isn't because it takes an argument name. 4:37 So here we'll say let nameValue = user. 4:41 And then we'll use the subscript notation like square brackets, that will 4:46 pass in an argument name, keyPath, and then provide the keyPath we just defined. 4:51 So here we've used the keyPath to retrieve the value of the name 4:56 property on user, on that user instance we created earlier. 5:01 If you option click the type of name, so earlier when we use the Swift 3 5:06 style of keyPath, and got the value using value for key. 5:11 When we option click on the value for name, 5:15 you'll see that the property is of type Any, it is also an optional. 5:18 Now in contrast, using Swift 4's keyPath, the type of name value is string. 5:23 Since the keyPath object contains information about the property 5:29 at the end of the keyPath, we're assured that we'll always get the right type back, 5:33 which in this case is a string. 5:38 If I explicitly cast this to a different type, I'll get an error. 5:39 That writing to the property using the keyPath uses the same style syntax and 5:44 should feel familiar. 5:50 So here we can say user keyPath and again, 5:51 we'll pass the nameKeyPath in and to this we can assign a value. 5:55 Now this is where having type information really helps. 6:01 So if I assign an integer value again, we get an error, but 6:04 this time it's a compiler error. 6:07 Can not assign value to type int, just type string. 6:09 So let's go ahead and change that. 6:14 Put this back to Pasan. 6:16 Now this is pretty cool, but we can do much more with keyPaths in Swift 4. 6:18 So let's define a new type, struct account, 6:23 and we'll give it a name of type String. 6:28 A property users and this is going to be an array of users, and 6:33 then finally an administrator which is going to be an optional user. 6:38 Let's go ahead and create an instance as well. 6:44 So let account = Account, name is Treehouse, 6:45 for the users we'll just pass in that 6:51 instance we created, and the admin is nil. 6:55 If we wanted to get a keyPath to the name, 7:00 this is no different from what we did earlier, so 7:04 let accountNameKeyPath = again, \Account, 7:08 the type, and then the property. 7:13 Notice that account is a struct, and we've successfully created a keyPath for 7:17 a value type, an object that's not an instance of NSObject. 7:22 So Swift 4's keyPaths are not reliant on the Objective C run time 7:27 unlike the Swift 3 implementation. 7:32 In fact, if we go back to the user type here, 7:34 the class we defined earlier, we can go ahead and 7:38 get rid of this NSObject inheritance, and all of these @objc bits, and 7:41 you'll see that while the Swift 3 stuff fails, the rest works. 7:47 So let's get rid of this. 7:52 Since we don't have a need for the Objective C runtime, 7:58 this code can also run on Linux. 8:02 Now I got rid of the instance by mistake, so 8:04 we'll just get rid of these two lines of code and the keyPath. 8:08 There we go. 8:13 If you go down to the keyPath we just defined and 8:18 option click on accountNameKeyPath, you'll see that the type here is simply keyPath. 8:21 In contrast to the ReferenceWritableKeyPath we got earlier, 8:27 which means again, that Swift can distinguish 8:31 whether the underlying keyPath points to a reference type or a value type. 8:34 Now we can do more than this though. 8:38 The account type has a property, admin that is an optional instance of user. 8:40 Let's say we wanted to get the username of the user that is serving 8:45 as an admin of this account. 8:50 So let's create a keyPath. 8:51 Not there, oops, down here. 8:53 We'll say, let adminUserKeyPath, again, \ we'll start with a type. 9:00 Then the properties admin. 9:07 And then we want the underlying user name. 9:09 Notice that since this is a type-safe keyPath, 9:14 we can also encode information in here about the optionality of the property. 9:17 So here admin is an optional type and is indicated by an optional property and 9:22 it's indicated in the KeyPath. 9:27 If you inspect the type, again this is a generic type. 9:28 If you inspect the type, 9:33 this is again a generic keyPath object on the Account type. 9:35 So it says Account here. 9:39 But then for the property if you look, 9:41 it indicates that the underlying property we access is an optional string. 9:43 This means that if we use this keyPath to retrieve a value, 9:48 let's say, let adminUsername and 9:55 we'll say account keyPath adminuserkeyPath. 10:00 If we do do this to read the value, when you inspect this, 10:06 you'll see that the type of this value is an optional string as well. 10:09 So we're able to encode a bunch of information using these keyPaths so 10:13 that we always have type safety. 10:17 Now we can also use keyPaths with arrays using subscript notation. 10:20 So let's say we wanted to get the name of the first user associated with this 10:24 account. 10:29 We can define a keyPath like this let 10:30 firstUserKeyPath = Account.users. 10:34 And then here we'll use the array subscript notation, and 10:39 pass in an index value to get the first user, and then get the name of that user. 10:43 Now this is relatively straightforward, but 10:48 you notice that you'll run into an error. 10:50 And at the time of this recording, it says here that keyPath support for 10:52 subscript components has not been implemented yet. 10:57 So we'll get it one day relatively soon. 11:00 So maybe by the time you get around to doing this, 11:03 it should already be implemented. 11:06 Now speaking of unimplemented features, 11:08 there's one more that will be added in the near future. 11:10 And that's the ability to infer the underlined type of the keyPath. 11:14 So every line of code here, that we've defined a keyPath, 11:19 we've specified the type by writing a \Account and so on. 11:24 When reading the value using a keyPath, so for example over here, 11:29 we provide the keyPath as an argument to an instance. 11:34 Since the type information is already provided in this instance value, 11:38 we know that we're accessing Account, and that Account here is of type Account. 11:43 By adding \Account to the keyPath, it's redundant, basically. 11:49 So since the type information is already provided we don't really need to redefine 11:54 it for the keyPath. 11:58 So if I were to do this again, I'm gonna paste this right below, 12:00 I'll paste it down here after commenting this out. 12:04 So if I were to do something like this and 12:08 say adminUsernameValue to distinguish between the two. 12:11 Instead of passing this keyPath in that I previously defined, I could say again \. 12:15 That's how we define keyPaths. 12:22 And instead of saying Account.name, here I can say .name. 12:23 Since the compiler knows that the account instance is of type Account, 12:30 we don't have to say Account.name and we can use the shorthand notation. 12:34 But again, this hasn't been implemented yet. 12:38 So I'm going to comment this out too. 12:41 There you have it. 12:44 The new keypad syntax introduces the type safety we've come to expect in Swift but 12:45 still allows for a familiar Coco paradigm. 12:50 I should point out that the earlier pound keyPath syntax that we looked at, 12:53 the Swift 3 version. 12:57 That is still going to be available for use with legacy string-based APIs. 12:59 As always, check the notes to read more about this proposal. 13:04
You need to sign up for Treehouse in order to download course files.Sign up