This workshop will be retired on May 31, 2020.
Migrating to Swift 319:57 with Pasan Premaratne
Learn how to migrate your projects to Swift 3.
Hi, I'm Pasan, an iOS instructor here at Treehouse. 0:00 And in this workshop, we're going to go over migrating to Swift 3 and Xcode 8. 0:04 Over the course of this workshop, we're going to talk about 0:08 why you should migrate, how you should migrate your codebase over. 0:11 And then spend some time looking at the major and minor changes to Swift 3. 0:16 Let's start with why you should consider migrating. 0:22 The first thing to note is that Swift is still not ABI-stable. 0:25 This means that application binaries created with previous versions of Swift 0:30 are not compatible with the current compiler. 0:35 ABI stability was originally a goal of the 3.0 release. 0:38 So it's important to be aware that it was pushed. 0:42 We've now been promised ABI stability in the 4.0 release. 0:45 There are good reasons to migrate to Swift 3 as well. 0:50 Swift 3 brings a much-needed consistency in API design across the language. 0:53 The changes are vast, which is where most of the migration pain will be apparent. 0:58 But the guidelines are clearly defined and give Swift a character of its own. 1:04 With Swift 1 and 2, 1:09 we've mostly been using a variation of Cocoa's design guidelines. 1:10 And it felt clunky in a lot of places. 1:15 With the goal of cross-platform swift, Cocoa conventions make even less sense. 1:18 So the new guidelines aim to resolve that. 1:23 We'll go over some of these high-level changes. 1:26 The new API guidelines have been applied to Objective-C APIs imported into Swift. 1:30 And along with better use of Swift's features, 1:37 it is a much nicer experience using Cocoa Touch classes in Swift. 1:40 We'll be taking a look at some of these examples as well. 1:45 A final reason for moving to Swift 3 is better tooling and language support. 1:49 Xcode 8 includes several new and improved features, 1:55 like a runtime issue report generator, a brand new memory debugger, 1:59 a thread sanitizer to spot race conditions, and an improved view debugger. 2:04 If your codebase is in Swift, 2:10 moving to Xcode 8 means making a decision on the language version. 2:12 So perhaps you know that you want to migrate to Swift 3, but 2:18 you're unsure about when you should do this. 2:21 How urgent is the need? 2:24 Swift 1 to Swift 2 was pretty urgent. 2:27 You could not use Xcode 7 unless you migrated your codebase, and 2:30 this was quite painful. 2:34 It meant choosing between migrating your codebase, 2:36 versus improving your app for users by adding new functionality and features. 2:39 With Xcode 8, Apple is a bit more forgiving, thankfully. 2:44 Xcode 8 supports both Swift 2.3 and Swift 3.0. 2:49 It's important to note, though, that we don't know how long that will last. 2:53 A future minor version of Xcode 8 may remove this functionality and 2:58 only support 3.0. 3:02 It really is intended to just give us more time to migrate over, 3:04 because the changes are pretty vast. 3:08 So while you do have the ability to use Xcode 8 with a codebase in Swift 2.3, 3:10 I would recommend getting started as soon as possible. 3:16 I've heard everything, ranging from Xcode will support 2 versions until Xcode 9, 3:20 to the next minor version will only support 3.0 and up. 3:26 Even if you decide that you are going to stick with Swift 2.3 for now, 3:31 your hand might be forced. 3:34 Most third-party libraries are well underway with Swift 3 versions and 3:37 have frozen their 2.3 versions. 3:42 You can expect bugfixes, but not much else. 3:46 Given the lack of ABI stability, if you use several dependencies, 3:49 the burden now falls on you to maintain a fork for as long as you need to. 3:53 While there's no urgency to this, another good reason to migrate is to take 3:58 advantage of the improved safety offered by improved Objective-C API in Swift 3. 4:03 Some of the improvements to the SDK in iOS 10 are available only through Swift 3. 4:10 One of my new favorite features is the usage of generics in Core Data API, 4:16 something we'll look at in later slides. 4:20 This, for example, is only available in Swift 3 for those of you writing Swift. 4:24 Let's say you've decided to migrate. 4:30 What are you getting yourself into? 4:32 Over the next few minutes, let's go over some of the major changes to the language. 4:34 This will be a quick overview. 4:39 The scope of some of the changes are extensive. 4:41 But I've provided links to resources at the end of this deck for you to check out. 4:44 We'll start by exploring the new API Design Guidelines, 4:48 particularly those that define the new naming conventions. 4:52 After that, we'll see how these guidelines have been imported to Objective-C API and 4:55 why they're much nicer to use in Swift 3. 5:00 Swift 3 also includes new types that makes both the cross-language and 5:03 the cross-platform development experience easier, and we'll touch on this. 5:08 Finally, we'll take a look at few of the minor changes and some strategies for 5:13 migrating. 5:16 The most apparent changes are those defined in the new API Design Guidelines. 5:18 Not only do they require you to think differently from naming 5:23 conventions established by decades of Cocoa and Cocoa Touch development. 5:27 But the application of these guidelines mean that usage of familiar API is 5:31 different now. 5:36 And this affects how our codebases are designed. 5:37 Let's start with the fundamentals. 5:42 These are overall guidelines that should help you understand the point of view of 5:44 the Swift core team. 5:48 And thereby, help you get in the mindframe of redesigning your own API. 5:49 Before we proceed, as we go through these slides, you'll notice in the bottom 5:54 right-hand corner a link in the format SE, followed by a number. 5:59 This is a link to the swift-evolution post on GitHub that provides an excellent, 6:04 in-depth summary of the changes we're talking about. 6:10 If you're interested in learning more about each change, 6:13 that's where you should go. 6:16 All code should be written so that it reads well. 6:18 But Swift 3 approaches this from a slightly different perspective than we 6:21 might be used to. 6:26 Objective-C classes have both a header and an implementation file. 6:27 API design in Objective-C was focused on making methods and 6:32 variables descriptive and informative through a header file. 6:36 We're all familiar with Objective-C's verbosity. 6:40 And this provided for a great deal of clarity and intent. 6:43 In Swift 3, the focus has shifted to clarity at the point of use. 6:47 We want to design API that is readable, concise, and clear when our methods and 6:52 functions are called, rather than when we look at a class' interface. 6:58 When Swift was released, the pendulum swung from one end, with Objective-C-style 7:03 verbosity, to the other end, where developers often focused on brevity. 7:08 Swift 3 makes it clear that brevity should not be a goal in API design. 7:14 Instead, it should be a side effect of focusing on clear API 7:19 that uses Swift's language features like the type system. 7:24 Essentially, at no point should you be focused 7:27 on writing the smallest amount of code. 7:30 Swift 3 also uses a variation of CommonMark, a dialect of Markdown, 7:33 to provide a consistent format around documentation and doc comments. 7:38 Since the focus on API design is at the point of use, 7:44 Swift 3 relies on documentation comments and 7:48 Xcode IDE features to provide full method descriptions in any related documentation. 7:50 As I mentioned, these API Design Guidelines are pretty vast. 7:57 So let's break it down and look at some of the new naming changes, so 8:01 you have an idea of what you're getting into. 8:04 One of the major goals of Swift 3 is to prevent ambiguity at the call site and 8:08 to promote clear usage. 8:12 This means including all the words necessary in the method signature 8:15 that are needed to convey intent. 8:18 Now, this sounds like Objective-C, but there are further restrictions. 8:20 So here, we have an example of API that would be deemed 8:25 ambiguous under the new guidelines. 8:28 We have a remove method that removes an element at an index position and 8:31 returns it. 8:36 At the point of definition in the class' interface, this reads well and 8:37 it's obvious what the method does. 8:42 However, remember that with Swift 3, the focus is at the call site. 8:44 At the call site here, it's unclear whether we're 8:48 removing a particular element, an element equal to some object, 8:51 or whether we're actually fiddling with the index. 8:55 Here is the same method formatted for Swift 3. 9:00 Swift 3 makes a departure from Cocoa and 9:03 Objective-C conventions when it comes to argument labeling. 9:06 Consistent label behavior has been established across all parameters, 9:10 including the first label. 9:14 Whereas in Swift 2 and 9:16 prior, the first argument label was omitted by default to match Cocoa. 9:17 Here, we've given the method an external argument, 9:22 at, to clearly indicate we're removing at a particular index. 9:25 We've also defined a typealias to provide increased clarity around the index value. 9:30 That first rule of including all the words made it sound like we were striving for 9:37 Objective-C's verbosity. 9:41 But that's not actually the case, because our second rule is to omit needless words. 9:43 That is, redundancy should not be a goal. 9:49 Here's a simple example. 9:53 Here, we have the signature for 9:55 a class method on NSLayoutConstraint that activates an array of constraints. 9:56 This would be considered poor API design in Swift 3, why? 10:02 We have a lot of redundant information here. 10:07 It's clear, from both the type on the argument as well as the label, 10:10 that we're activating constraints. 10:14 Yet we're being redundant by specifying this in the base method name. 10:16 This isn't as bad in Swift 2, because that argument label would be omitted. 10:21 But in Swift 3, that's a lot of times that we're saying constraint. 10:25 One in the class name, one in the base method name, one in the argument label, 10:29 and the final time through the type. 10:34 Here is the same method in Swift 3. 10:37 The method name is now simply activate. 10:39 We're relying on a Swift feature, 10:43 the strong type system, to clarify what it is we're activating. 10:45 Because the type is sufficient enough, anything else is redundant. 10:50 So we've intentionally omitted the external argument label as well. 10:54 The final method at its call site reads as a grammatical English phrase, 10:58 which is one of Swift's goals. 11:03 NSLayoutConstraint.activate(constraints). 11:05 Speaking of English phrases, 11:10 there are several rules governing naming conventions. 11:12 I highly recommend you take the time to go through the guidelines carefully. 11:15 Both in the proposal linked below and 11:20 the official guidelines page linked at the end of this deck. 11:23 New naming conventions affect our codebase in two ways. 11:28 One in the code we write, and 11:32 second, the code that we call from Cocoa Touch frameworks. 11:34 The application of new guidelines affects changes to both how Objective-C, 11:38 and even C methods and functions, are imported into Swift. 11:43 As well as the usage of these methods by making use of Swift-only features. 11:47 One of the more obvious changes that makes for a great example is Core Data API. 11:53 In Swift 3, NSFetchRequest is a generic type 11:58 with a type parameter that conforms to a new protocol, NSFetchRequestResult. 12:02 This means that given an NSManagedObject subclass Foo, 12:09 we can create a FetchRequest for Foo like this. 12:13 No more stringly-typed entity names. 12:16 Furthermore, if we look at the signature for 12:20 the fetch method, you'll notice that the return type is not AnyObject, 12:22 as it was in Swift 2, but the type of the generic parameter. 12:26 This means that if we call fetch on a context and provide a FetchRequest, we're 12:31 guaranteed to get an array of the type back, rather than an array of any object. 12:36 You'll notice, throughout this example as well, 12:42 that the new guidelines have been applied to existing Objective-C API for 12:44 a more consistent naming experience in Swift. 12:48 Speaking of Any and AnyObject, 12:52 Swift now imports any API typed with ID as Any, rather than AnyObject. 12:54 For example, here's a bit of code from the NSFetchedResultsController delegate 13:01 protocol, sticking with our Core Data example. 13:06 Notice that the object changed over here as an argument type of AnyObject. 13:09 In Swift 3, this comes in as Any now. 13:16 The motivation behind this change is to keep Swiss value semantics consistent 13:20 when importing Objective-C API. 13:24 It's worth reading the entire proposal to understand the scope of this change. 13:28 The reason I bring this up is because the migrator doesn't always play nice. 13:33 So it's important to be aware of bugs in Xcode that can affect you. 13:37 So here's my experience. 13:41 I recently migrated a project from Swift 2 to Swift 3. 13:43 The migrator seemingly did its job. 13:45 But there were a few cases where it did not convert the types of such 13:49 imported API from AnyObject to Any. 13:54 There were no compiler errors raised by the inability to convert these methods. 13:57 But because the signature of this particular delegate method did not match 14:02 the signature of the one in the protocol, my delegate method was never being called. 14:07 What's more surprising is that there was no compiler error informing me that I was 14:12 not fulfilling the delegate protocol contract. 14:16 Took me a while to track that down. 14:20 In much the same way, in sets and dictionaries, NSObject and 14:23 AnyObject are now imported as AnyHashable and Any. 14:28 This is important to know, because if you are writing Objective-C code or 14:32 are a library author, there are new Objective-C features to control and 14:37 adapt this importing. 14:41 All these features make the experience of working with both languages much better. 14:44 And should hint at a future where neither language is really 14:48 preferred over the other, but up to the developer. 14:51 There are several changes around this goal, including one of my favorites, 14:55 proposal number 33, which imports Objective-C constants as 14:59 Swift types like enums. 15:04 One of the more interesting changes to Swift is the overhaul of the standard 15:08 library. 15:11 This was motivated both by mutability concerns and 15:13 the lack of foundation types in a cross-platform experience. 15:16 NSDate now has a Swift equivalent, Date, URL for NSURL, and so on. 15:20 These new types are value types and play well with Swift's mutability semantics. 15:27 You might run into minor errors during a migration process, where you were 15:32 previously mutating a reference type that has now been bridged to a value type. 15:37 But this is a good thing, in my opinion. 15:41 You're aware of this error up front. 15:43 Note that you are not required to only use the new foundation types, but 15:45 have access to the legacy Objective-C types as well. 15:49 These types also play well with each other and 15:53 are automatically bridged as you move from one language to the other. 15:55 Libdispatch is also improved in Swift 3. 16:01 Here, we see an example of libdispatch in Swift 2. 16:04 In the first snippet, 16:08 we call dispatch_queue_create to create a custom queue. 16:09 And then call dispatch_async and pass in the queue to execute some arbitrary code. 16:12 In the second snippet, we're executing some arbitrary code on the main_queue. 16:18 In both these situations, the libdispatch library is imported as is. 16:23 And the C API seems completely out of place. 16:28 In Swift 3, the API surface has been transformed to feel much more natural. 16:33 We now have a DispatchQueue type with class properties 16:38 to return various common queues. 16:42 We also have instance methods that dispatch our code, either synchronously or 16:44 asynchronously. 16:49 While a true concurrency model is still a ways off in Swift 4, 16:50 this is a much better experience. 16:54 Let's look at one last change that makes a pretty big impact when migrating. 16:58 Swift 3 brings some new access control keywords and 17:03 changes the meaning of existing ones. 17:06 One of the new access control keywords is fileprivate. 17:09 A fileprivate variable is only visible inside the file. 17:13 This means that you can use fileprivate inside a particular Swift file and 17:17 across any extensions of the type in that file. 17:21 But this is what private used to mean. 17:26 Well, private now means private to the current scope. 17:28 A variable defined as private cannot be accessed in an extension of the type. 17:32 A second proposal, 117, 17:38 introduces a new keyword, open, and changes the meaning of public. 17:40 In Swift 2, declaring a class or variable as public 17:46 meant that external modules could use the class and override it. 17:50 In Swift 3, 17:54 public now means that external modules can use the class, but cannot override it. 17:56 Instead, a new keyword has been introduced, open. 18:01 That means a class can be used and overridden by an external module. 18:04 These newer keywords will certainly impact your codebase as you migrate to Swift 3. 18:09 Many of the variables, constants, and functions marked as private, but 18:15 used in extensions, will now return compiler errors. 18:19 Essentially, there's a bit of cleanup to do. 18:22 As you can see, there are lots of changes you will have to make, 18:26 both to your code and the code you call. 18:29 And we've only covered some of the major changes. 18:32 There are several minor changes as well that'll undoubtedly impact you. 18:35 So how should you go about it? 18:39 The first thing is, you should ensure that you have decent test coverage. 18:42 You'll want to know that the codebase behaves in the same way after migration. 18:47 So make sure you have benchmarks that inform you as such. 18:51 Next, start your migration by using the built-in migrator. 18:55 It's far from perfect, but 18:59 it will take care of most of the renaming of API from frameworks. 19:01 But beware, 19:06 of course, there might be some edge cases that you'll have to take care of. 19:06 Set aside a decent amount of time. 19:11 The scope of changes is pretty vast. 19:14 It's more painful going from Swift 2 to Swift 3, than Swift 1 to Swift 2. 19:16 And you have to do it all at once. 19:20 So don't try to get this done while hitting other deadlines as well. 19:23 Finally, and 19:28 probably a bit more painful, is to figure out a strategy for all your dependencies. 19:29 Most third-party libraries have Swift 3 branches that you can already pull in. 19:34 But the situation may vary on a case-by-case basis. 19:38 While you're certainly not in a great rush to get this done, 19:43 remember that any Swift 2 code written right now is technical debt. 19:46 So you want to try and get this done as soon as possible. 19:50 I've included a few resources so you can read up on the scope of changes. 19:53
You need to sign up for Treehouse in order to download course files.Sign up