This workshop will be retired on May 31, 2020.
What's New in Swift 2.025:40 with Pasan Premaratne
In this video, we take a high level view at some of the changes introduced in Swift 2.0 as well as improvements to existing features.
[MUSIC] [SOUND] 0:00 [MUSIC] 0:00 [SOUND] 0:01 [MUSIC] 0:02 [SOUND] Hi everyone. 0:03 As you might have heard, 0:06 Apple announced Swift 2.0 at its developer conference, WWDC 2015 in June. 0:07 In this workshop, we're going to take a look at what's new in Swift, and 0:14 what changes we need to make to our code. 0:18 Now, this workshop will be a little unorthodox. 0:21 When Apple announces new technology at its conference every year, the pre-release 0:24 software that we get access to, comes with a non-disclosure agreement. 0:29 This year's agreement allows us to talk about Swift, iOS 9, and the new APIs. 0:33 But we can't post screen shots, or screen casts of Xcode 7, 0:39 and we need Xcode 7 to run Swift 2.0. 0:43 So we're going to take a look at all the new stuff using good, 0:46 old presentation slides. 0:50 Now for reference in this workshop every time I say Swift, I mean Swift 2.0. 0:52 If I'm referring to a different version, I will explicitly mention it. 0:57 Also, you can download this entire workshop as a playground 1:02 from the teachers notes, and just read it if you would much rather do that. 1:05 Let's get started. 1:10 Now the first, [SOUND] and most obvious change to Swift, 1:11 is how we print values to the screen. 1:14 Until now, we've used the println() statement. 1:17 This now has been shortened to print(). 1:20 As simple as this change is, I wanted to bring it to your attention, because when 1:23 you open up your projects in Xcode 7, there will be errors for every single line 1:28 of code where you print a value, because println() has been deprecated. 1:32 [SOUND] Swift 2.0 also brings a fair bit of change to 1:38 some of the control flow constructs. 1:41 Let's take a look at each. 1:43 A guard statement like in this statement, executes the statements within its scope, 1:45 depending on the boolean value of some expression that we evaluate. 1:51 [SOUND] In a standard if statement, if the boolean expression is true, 1:55 the code inside the braces is evaluated. 2:00 [SOUND] In a guard statement however, if the expression being evaluated is true, 2:03 [SOUND] the code after the guard statement is executed. 2:08 If the expression is false, the code inside the braces is executed. 2:12 Unlike an if statement, guards always have an else clause, and 2:17 it is inside this else clause that we put code to evaluate 2:21 when the expression being evaluated returns false. 2:25 Let's look at some code we would have written using Swift 1.2. 2:28 [SOUND] Here we have a Person struct, and a function, 2:32 that giving a JSON Dictionary, returns an optional instance of person. 2:36 Like with any data that we get from an external source, there's no guarantee, 2:41 that it will contain all the necessary information. 2:45 [SOUND] Our Person struct has three properties. 2:48 Name, age, and address, of which name, and age can't be nil. 2:52 So when we query the JSON Dictionary, we need to make the name, 2:57 and age exist before we create, and return an instance of person. 3:00 [SOUND] So in this case, 3:05 we use if let binding to check for the existence of those values. 3:06 Unwrap it, and use it to create an instance of person. 3:11 If those keys don't exist in the dictionary, we return nil. 3:15 However, there's an issue here. 3:19 [SOUND] We don't expect all instances of Person to have an address, 3:20 hence the optional property. 3:24 But in our code, we're specifying that the key 3:27 address needs to always return a value, so that we can create an instance of Person. 3:29 This means that if the optional address value is nil, Person is never created. 3:35 So let's try, and rewrite this again in Swift 1.2. 3:41 Now in this case, [SOUND] we're separating the checks for 3:43 each key value pair rather than chaining it. 3:47 [SOUND] We first check for a name. 3:50 If name is nil, well, we just return a nil immediately because we always want a name. 3:52 [SOUND] Now, the same goes for age. 3:57 Finally we check for an address, and once we have all of three properties, 3:59 we can create an instance of Person. 4:04 This code is guaranteed to create a valid instance of Person every time 4:06 because we just exit the function, if we don't have the required values. 4:11 However, it's less than ideal. 4:15 For starters, it's hard to read the flow of our code. 4:17 In the previous example, the logic we desired was nested within an if statement. 4:20 In this particular example, 4:25 we managed to keep the main flow of code nested in one level within the function. 4:27 But because of that, we have to unwrap the optionals when creating the instance of 4:32 Person, and we have these ugly exclamation points. 4:37 So, let's look at this using guard. 4:40 [SOUND] Instead of using an if let statement, we use a guard let statement. 4:42 [SOUND] Like an if let, we can check for 4:46 the existence of values, unwrap them, and assign them to constants. 4:49 However with a guard let, if everything goes okay, 4:54 [SOUND] the execution continues after the braces. 4:57 This means that we can create an instance of Person within the normal function 5:01 scope without having to use banks to unwrap these values. 5:06 Now if the values don't exist, that is either name, or age, or 5:10 nil, then execution jumps to the else clause. 5:14 [SOUND] And we exit the function early, returning nil. 5:18 [SOUND] So let's recap, [SOUND] if the guard statement's condition is met, 5:22 then our execution continues afer the closing brace of the guard statement. 5:26 [SOUND] Now if the expression evaluates to false, 5:31 then we jump inside the else branch, and evaluate that bit of code. 5:34 [SOUND] Keep in mind though, that when we're inside the else branch, we need 5:38 to use a control transfer statement such as return, break, or continue. 5:43 Or we can call a function, or a method, 5:48 that doesn't return to the normal scope of execution inside the function. 5:50 [SOUND] The next control flow statement that's new in Swift is 5:55 the Repeat-While statement. 5:59 This isn't actually anything new, it's just a case of syntax change. 6:01 [SOUND] In Swift 1.2 we had a Do-While statement. 6:06 In a Do-While statement, the loop performs a single pass through the body 6:10 before considering the loop's condition. 6:14 If the condition was true, then the loop would be executed again. 6:17 There are two reasons, why this was changed. 6:21 For one, it wasn't immediately clear that this was a repeating loop. 6:24 Do doesn't signify clearly, 6:28 that the code within the braces would be executed more than once. 6:31 And this is especially true, if there is a lot of code within the brace meaning, 6:34 that we can't see the end that marks the while statement. 6:39 To account for this, the key word do was changed [SOUND] to repeat. 6:43 Now it's pretty obvious, that this is a repeating loop. 6:47 The second reason it was changed from do, 6:51 is because do now has a different purpose, and we'll look at that a bit later. 6:53 Enough about control flow. 6:59 Let's take a look at some of Swift's new pattern matching features. 7:01 Now, we've used pattern matching before in Swift 1.2. 7:04 [SOUND] Here's a quick example. 7:08 [SOUND] We have a constant that stores a countryCode as a string. 7:10 And then using a switch statement, 7:14 we perform different actions based on the value of the countryCode constant. 7:16 Now, this should all be familiar to you. 7:22 [SOUND] Now, a switch statements could do more. 7:23 And using pattern matching we could temporarily [SOUND] bind values to local 7:26 constants, or variables, and then use them in different cases. 7:31 Here is an even more complex example. 7:36 He have an enum [SOUND] Barcode, that has two members. 7:38 A UPCA Barcode, that has an associated value of four Ints. 7:41 And a QRCode, with an associated value of a string. 7:46 [SOUND] We can then create an instance of the UPCA Barcode, and 7:49 assign it initial values. 7:54 Like before, [SOUND] we can use a switch statement, but 7:56 we can use complex pattern matching rules to filter on very specific cases. 8:00 So not only can we switch on each enum case, 8:04 and temporarily bind its associated values to local constants, 8:07 [SOUND] but we can also provide constraints using the where keyword. 8:12 So this case is only evaluated, 8:16 if we have a UPCA Barcode whose numberSystem's associated value is 8. 8:19 But this can get cumbersome. 8:25 What if, we only wanted to check for one particular case. 8:26 Let's say we only wanted to check, if the productBarcode variable was 8:30 an instance of QRCode, and we didn't care otherwise. 8:35 Well now in Swift 2.0, we can. 8:38 [SOUND] Pattern matching has been brought over to other control flow statements 8:41 as well. 8:46 So we can do an if case statement, and check just for a single case. 8:47 In this example, we're checking to see that [SOUND] it is a type of Barcode, 8:52 with a member QRCode, where the productCode that is the locally bound 8:57 constant pertaining to the associated value equals some string. 9:01 Pretty powerful stuff. 9:06 [SOUND] Now, this pattern matching power has also been brought over to for loops. 9:07 In Swift 1.2 given a array of numbers, if I wanted to print out the even numbers, 9:12 we could write something like this. 9:17 We would iterate over the array, and then inside the body of the loop, 9:20 we could write an if statement to filter through the results. 9:23 In Swift 2.0, we don't need that inner if loop. 9:26 We can evaluate an expression as a constraint, 9:31 directly in the loop definition using the where keyword. 9:33 [SOUND] We also have the ability to check for different cases. 9:37 So in this example, we have an enumeration that defines driving directions. 9:41 We have an array of directions called movement, and 9:46 we want to filter out just the right turns. 9:49 Now, rather than iterating through the entire array, and 9:51 adding a constraint within the loop We can check that we only drop through to 9:55 the body of the loop when it is a right turn by using the case keyword. 10:00 So that's it for pattern matching in Swift. 10:04 Switch statements allowed you to filter on some complex predicates, or 10:07 rules, and now we have that power built into if statements and for loops as well. 10:11 Which means that we can write more complex control flow in fewer lines of code. 10:16 One of the big new additions to Swift is the built-in support for error handling. 10:21 Now with all the content on the site, at this point at the time of this recording, 10:26 we haven't really dug into error handling before. 10:30 So let's take a brief look at how we currently do things. 10:33 What was cumbersome about those methods. 10:37 And how Swift 2.0 improves on this. 10:39 Remember that until just last year, Cocoa and 10:43 Cocoa Touch operated purely in the world of Objective-C. 10:46 Well, Objective-C can't return multiple values from a method or function. 10:50 And so in cases where we need it to notify callers of an error, 10:55 there was an established way of handling it. 10:59 Errors were handled with the NSError class. 11:01 And for a method or function that needed to return an error, 11:04 we put an error parameter in the function or method definition. 11:08 Within the body of a function, if there was an error, the error object 11:12 passed in as a parameter, was populated with information about the error. 11:16 Now in this method, we're also returning some object, so 11:22 we can't return the error object. 11:25 What do we do? 11:27 Well, when calling the function, we first create an error object. 11:29 And then pass in a reference to the error object rather than the object itself. 11:33 What this means, if you're not familiar with references, 11:39 is we tell the method, this is the space in memory where that object lives. 11:42 Now this is denoted in the method signature by using the inout keyword. 11:46 So because the method knows where the error object lives in memory, 11:51 it adds some information directly to it and then stores it in the same space. 11:56 By convention, if you hit an error this way, you would return nil for the object. 12:00 Then when calling the method, given that an array means a nil object, we first 12:05 check that the object isn't nil, and if it is, then we check what error occurred. 12:11 This workflow is full of pitfalls. 12:16 First, this is an established pattern of error handling, but 12:19 there's no compiler support. 12:22 So you aren't enforced when it comes to presenting or checking for errors. 12:24 Second, there's a lot of conventions that you need to be aware of. 12:28 When the method returns an object and hits an error, well, then you return nil. 12:32 If it's a boolean though, then you return false, and so on. 12:36 You need to know, with each particular method, what to check for, whether it's 12:40 nil, or false, or something else, and then when that method contains an error object. 12:45 It's all very confusing stuff. 12:50 Let's look at a more common example. 12:52 In some of our courses we've made network requests. 12:54 And if you remember, the completion handler in the data task with the URL, or 12:57 data task with request methods, contained an error object. 13:01 In the course I said that, for the sake of brevity, 13:05 we weren't going to check on errors and I left it up for you to do so. 13:08 This is yet another pitfall. 13:13 Since there's no compiler enforcement, 13:15 it becomes all too easy to simply ignore error handling. 13:18 Okay let's take an example, and 13:22 see how it's improved with the new error handling features introduced in Swift 2.0. 13:23 In this example, we want to parse some data from a file, and into a dictionary. 13:28 Converting from a data object to a dictionary format is relatively easy using 13:34 the built-in JSONObjectWithData class method on the NSJSONSerialization class. 13:39 Now this method could fail, and 13:46 we indicate this in our method by using an inout error parameter. 13:48 Then we pass this error by reference to the JSON parsing method. 13:53 And if the error isn't nil, that is if we can't successfully parse the data, 13:59 then we return nil for our method's return value. 14:04 Otherwise, if everything's okay, we return a dictionary. 14:08 So to start, let's get rid of the error parameter in our method declaration. 14:12 This means were not passing in an error object, which is good. 14:18 But we don't have to indicate any way to a caller anymore 14:22 that this particular method could return an error. 14:25 Similar to how we eliminated the error parameter, now Cocoa Touch methods, 14:28 under this new error handling model, also get rid of their error parameters. 14:33 Finally, since we don't have any error parameter, 14:38 we don't need to check that it is nil. 14:42 Now, even though we've gotten rid of these error parameters, 14:44 this doesn't mean our code is error free. 14:48 Remember the JSONObjectWithData can still fail. 14:51 Well, in Swift 2.0, any time you have an API that can fail, 14:54 you have to use the try keyword. 14:59 The try keyword indicates that the function can throw an error. 15:02 And then the lines of code after the try might not run. 15:07 Okay, now that we've annotated the failing method, how do we indicate that our 15:11 loadDictionaryFromFile method could fail as well? 15:15 To indicate that a function or method can throw an error, 15:19 you write the throws keyword in its declaration after its parameters. 15:23 If the function specifies a return type, 15:28 you write the throws keyword before the return arrow. 15:30 A function, method, or closure cannot throw an error 15:33 unless explicitly indicated by annotating it with the throws keyword. 15:38 This is much cleaner. 15:42 Now when you're looking at a message signature, you don't need to 15:44 parse the list of arguments to understand if the function can throw an error. 15:47 This is easily indicated by the throws keyword. 15:52 In Swift 2.0, all existing Cocoa Touch APIs have been bridged and 15:55 use this keyword. 16:00 Now that we have this method defined, 16:01 let's look at what has changed when we actually use it. 16:03 Notice first that we don't have the error parameter any more. 16:07 The method signature is much cleaner. 16:11 And we're only passing in the data object. 16:14 Remember earlier when we spoke about the repeat Y loop? 16:17 I said do had a different purpose in Swift 2.0, right? 16:20 Well with the new error handling model, whenever we have any code that can fail, 16:24 we wrap it in a do clause. 16:28 Inside the do clause we add the function that throws and 16:31 use it with the try keyword. 16:35 If the function works, 16:37 the rest of the statement within the do clause is executed. 16:39 But what if we hit an error? 16:42 If an error is thrown, that error is propagated to its outer scope 16:44 until it is handled by a catch clause. 16:49 Inside the catch clause we can handle any error code. 16:51 The catch clause also includes pattern matching, so 16:56 we can temporarily bind the error to a local variable or a constant. 16:59 Like a switch statement, 17:03 the compiler attempts to infer whether catch clauses are exhaustive. 17:05 We'll see what this means in a bit. 17:09 So this is the new error handling model in Swift. 17:11 In summary, we mark functions that can throw an error with the throws keyword. 17:15 When using that function or method, we write try in front of the call to 17:20 indicate that the lines of code after the try might not be run. 17:25 Finally, to handle the errors, we use a do catch statement. 17:29 We wrap the throwing function and 17:33 any subsequent related statements within a do clause. 17:35 If an error is thrown, it is propagated to the catch clause for handling. 17:39 Now that we know about handling errors, how do we express and 17:45 return errors of our own? 17:49 Errors in Swift are represented by any type conforming to the ErrorType protocol. 17:51 Enums in Swift are quite well suited to handling this task. 17:57 An enum comprises of a finite set of values. 18:01 And because a particular function or 18:04 method can have a finite set of errors, we can use an enum to encapsulate this. 18:06 The example from Apple's book is quite nice and very simple, so 18:11 I'll just use that. 18:14 We want to model a vending machine. 18:16 And there are a certain set of errors we can get when 18:19 interacting with a vending machine. 18:21 Error objects in Swift are anything that conforms to the ErrorType protocol. 18:23 So here we have a VendingMachineError enum with three cases, 18:28 InvalidSelection, InsiufficientFunds, and OutOfStock. 18:33 Three simple error cases that we could potentially run into. 18:37 So let's look at how we can use this in a simple app. 18:41 We have an item model that keeps track of the price of an item and its quantity. 18:45 We also have an inventory dictionary that maps names of products 18:50 to that particular item. 18:53 Finally, we have a variable that keeps track of the money that we have to spend. 18:55 Let's start with a vend function that allows a user to grab something from 19:00 the vending machine. 19:03 Well, as we just saw, 19:05 we can run into some potential errors when trying to obtain an item. 19:06 So let's mark this function with the throws keyword. 19:10 Now we need to carry out some checks. 19:13 In this function we're passing in the name of the item we want to retrieve. 19:15 So first we check whether this item exists in our inventory and 19:19 retrieve that particular item object. 19:23 To do this, we're using a guard statement, because if this fails, 19:26 then we can throw an error inside the else clause. 19:30 Notice that we use the keyword throw 19:33 in contrast to the throws keyword which we use to mark the function. 19:36 Okay, so in this case, if the item doesn't exist, 19:40 we use throw to throw the invalid selection error. 19:43 If the item exists, though, execution continues in the regular scope. 19:46 Now we can check that the item quantity is greater than zero. 19:51 Again, we use the guard statement, because if it's not, 19:55 well we can throw the out of stock error. 19:58 If we tried to perform this logic using an if else statement, 20:01 then we would have had to nest it because this checks come one after another. 20:04 The guard statement allows for a much cleaner syntax. 20:09 Finally, we check that we have the right amount of funds. 20:13 If we do, we get the item and update the balance. 20:16 If not, we throw the insufficient funds error. 20:19 Notice that since this is the last statement in the body of the function, 20:22 we're using a standard if else statement, and throwing the error as our final step. 20:26 With the function defined, let's use it. 20:31 Since this is a function that can throw, we're going to add it to a do clause. 20:33 We use [SOUND] the try keyword to call the throwing function. 20:38 If this works then yay, we have a delicious snack and move on. 20:41 If not, we propagate the error to the catch clause. 20:46 Now, just like before, we can use pattern matching to bind the error. 20:50 But just getting the error object doesn't provide much information. 20:53 In fact, we can do better. 20:57 We can pattern match and catch on a particular error, like invalid selection. 20:59 In fact, we can add multiple catch clauses, 21:04 to take every single error case into account. 21:07 We can even bind the amount required to a local variable if we need to. 21:10 And that is the new error handling model in Swift. 21:15 It's a lot more robust, in that it is compiler enforced, so 21:18 you can't simply ignore the error cases. 21:22 It's also a lot more obvious about which methods and 21:25 functions can fail by using the try keyword. 21:28 Cool, let's move on. 21:31 There's still some more to cover. 21:33 We yet another new keyword in Swift 2 called defer. 21:35 Defer is actually quite simple to understand. 21:39 [SOUND] We use defer to execute a set of statements just before you leave 21:42 a current block of code. 21:46 Let's look at an example. 21:48 So first, we have the defer keyword. 21:51 Within a set of braces, we include the statements to be executed later. 21:53 The only the restriction with deferred statements, 21:57 is that they may not transfer control. 22:00 That is you can't use break, or 22:02 return, or throw an error in there, that can change the scope of execution. 22:04 [SOUND] In this example, we have a quitGame function. 22:10 When someone calls the quitGame function, we want to save the game state. 22:13 So, we carry out whatever clean up actions we want within the body of the function. 22:17 Regardless of what happens in the body of the function, whether it's cleaning up or 22:23 whether we hit an error, we always want to call saveGame 22:27 when we're about to leave this function's scope of execution. 22:31 Traditionally, we would look at different ways we could leave the scope using 22:34 various if clauses and call saveGame in each case. 22:38 With the defer statement, you can say, well regardless of how you're leaving this 22:41 scope of execution, just call saveGame last, and that's all there is to it. 22:45 You can add as many defer statements as you want within some particular scope and 22:50 they will be called in the order that they are written. 22:54 [SOUND] Now, the last change to Swift I want to cover is Protocol Extensions. 22:56 This is actually quite a big topic that merits its own discussion, 23:01 so I'll just go over some of the more marketing details and 23:05 we'll dig into what this means in a separate course. 23:08 Part of the reason for doing this is that it's a big change. 23:11 But, more relevant to us, Protocol Extensions cover some ground 23:14 that requires understanding of topics that we haven't looked at yet. 23:18 At the time of this recording at least. 23:21 We still haven't gone over extensions so 23:24 for some of you, an extension to a Protocol might not mean a lot. 23:26 [SOUND] In the protocols course we looked at one of Apple's examples, 23:31 a random number generator. 23:34 The protocol defined a function random that returned a double that 23:36 conforming types needed to implement. 23:40 Now with a protocol extension, you can now add requirements to an existing protocol. 23:44 Not only that, you can provide default implementations for 23:49 protocol methods as well. 23:53 This is a pretty big change. 23:55 We discussed that protocols only defined requirements but 23:57 didn't actually implement them. 24:01 Well, as you can see, that's changed now. 24:02 And we will be looking at all of this in a separate course. 24:04 Protocol extensions means that you can add functionality 24:07 including default implementations to Apple's standard library of protocols. 24:11 All collection types like array and 24:16 dictionary conform to the collection type protocol. 24:17 By adding a protocol extension to collection type and 24:21 providing some default implementation, for a method of your choosing. 24:24 You can add functionality to all the data structures in Swift 24:28 that conform to collection type. 24:32 This is quite powerful. 24:34 For our perspective, one of the more noticeable changes because of this feature 24:36 is to standard library functions like sort. 24:41 Previously we passed an array into the sort function for sorting, but 24:44 now with protocol extensions, 24:48 sort is simply a method that you call on an instance of an array. 24:51 Now, we'll make the changes in older courses to point this out so 24:55 that you're aware of the fact. 24:58 [SOUND] So that was a brief roundup of some of the newer features in Swift. 25:00 There were lots more features announced that make it 25:04 much easier to work with Swift including better inoperability with Objective-C. 25:07 Better playgrounds and migrator in X code to bring your project from 25:12 Swift 1.2 to Swift 2.0 automatically. 25:17 [SOUND] Even crazier Apple announced that Swift will be open sourced later 25:20 this year. 25:24 This provides an even greater incentive to learn Swift because soon 25:24 you will be able to write not only IOS and Mac apps in Swift. 25:29 But possibly Web and even Android as well. 25:33 Check the teacher's note section for links to read up on all the new features. 25:36
You need to sign up for Treehouse in order to download course files.Sign up