Hacking With Swift
Short Description
Hacking With Swift...Description
PROJECTS 1-39
Contents About this Book
11
Introduction: Swift for Complete Beginners
15
How to install Xcode and create a playground Variables and constants Types of Data Operators String interpolation Arrays Dictionaries Conditional statements Loops Switch case Functions Optionals Optional chaining Enumerations Structs Classes Properties Static properties and methods Access control Polymorphism and type casting Closures Wrap up
Project 1: Storm Viewer
96
Setting up Deleting skeleton code Listing our images Introducing Interface Builder Sending new data Final tweaks Wrap up
Project 2: Guess the Flag
135
Setting up
www.hackingwithswift.com
2
Designing your layout Making the basic game work Guess which flag? From outlets to actions Wrap up
Project 3: Social Media
166
About technique projects Activity View Controllers Twitter and Facebook Wrap up
Project 4: Easy Browser
176
Setting up Creating a simple browser Choosing a website Monitoring page loads Refactoring for the win Wrap up
Project 5: Word Scramble
199
Setting up Reading from disk Pick a word, any word Prepare for submission Returning values Or else what? Wrap up
Project 6: Auto Layout
229
Setting up Advanced Auto Layout Auto Layout in code Sizes, metrics and priorities Wrap up
Project 7: Whitehouse Petitions
244
Setting up Creating the basic UI Parsing JSON Rendering a petition Finishing touches Wrap up
www.hackingwithswift.com
3
Wrap up
Project 8: 7 Swifty Words
267
Setting up Buttons… buttons everywhere. Loading a level It's play time! Property observers Wrap up
Project 9: Grand Central Dispatch
285
Setting up Why is locking the UI bad? GCD 101 Back to the main thread Wrap up
Project 10: Names to Faces
295
Setting up Designing UICollectionView cells Data sources and delegates Importing photos Custom classes Connecting up the people Wrap up
Project 11: Pachinko
317
Setting up Falling boxes Bouncing balls Spinning slots Collision detection Scores on the board Special effects Wrap up
Project 12: NSUserDefaults
347
Setting up Reading and writing basics Fixing Project 10 Wrap up
www.hackingwithswift.com
4
Project 13: Instafilter
358
Setting up Designing the interface Importing a picture Applying filters Saving to the photo library Wrap up
Project 14: Whack-a-Penguin
376
Setting up Getting up and running Penguin, show thyself Whack to win Wrap up
Project 15: Animation
398
Setting up Preparing for action Switch, case, animate Transform! Wrap up
Project 16: JavaScript Injection
412
Setting up Making a shell app Adding an extension What do you want to get? Establishing communication Hacking with JavaScript Fixing the keyboard Wrap up
Project 17: Swifty Ninja
434
Setting up Basics quick start Shaping up for action Enemy or bomb? Follow the sequence Slice to win Game over, man Wrap up
www.hackingwithswift.com
5
Project 18: iAd and Debugging
474
Setting up iAd in 10 minutes Debugging in Xcode Wrap up
Project 19: Capital Cities
486
Setting up Up and running with MapKit Annotations and accessory views Wrap up
Project 20: Fireworks Night
498
Setting up Ready... aim... fire! Swipe to select Making things go bang Wrap up
Project 21: Local Notifications
517
Setting up Scheduling notifications Acting on responses Wrap up
Project 22: Detect-a-Beacon
528
Setting up Requesting location Hunting the beacon Wrap up
Project 23: Space Race
540
Setting up Space: the final frontier Bring on the enemies! Making contact Wrap up
Project 24: Swift Extensions www.hackingwithswift.com
553 6
Setting up Adding to integers Cleaning up the mess Extensions for brevity Wrap up
Project 25: Selfie Share
567
Setting up Importing photos again Going peer to peer Invitation only Wrap up
Project 26: Marble Maze
584
Setting up Loading a level Tilt to move Contacting but not colliding Wrap up
Project 27: Core Graphics
605
Setting up Creating the sandbox Drawing into a context Ellipses and checkerboards Transforms and lines Images and text Wrap up
Project 28: Secret Swift
628
Setting up The basic text editor Writing somewhere safe Touch to activate Wrap up
Project 29: Exploding Monkeys
642
Setting up Building the environment Mixing UIKit and SpriteKit Unleash the bananas! Destructible terrain
www.hackingwithswift.com
7
Wrap up
Project 30: Instruments
673
Setting up What are we working with? What can Instruments tell us? Fixing the bugs Wrap up
Project 31: Multibrowser
687
Setting up UIStackView by example UIStackView in Swift 2 Removing views from a UIStackView iPad multitasking in iOS 9 Wrap up
Project 32: SwiftSearcher
708
Setting up Automatically resizing UITableViewCells with NSAttributedString and Dynamic Type How to use SFSafariViewController to browse a web page How to add Core Spotlight to index your app content Wrap up
Project 33: What's that Whistle?
733
Setting up Recording from the microphone: AVAudioRecorder Animating UIStackView subview layout Writing to iCloud with CloudKit: CKRecord and CKAsset A hands-on guide to the CloudKit dashboard Reading from iCloud with CloudKit: CKQueryOperation and NSPredicate Working with CloudKit records: CKReference, fetchRecordWithID, and saveRecord Delivering notifications with CloudKit push messages: CKSubscription and saveSubscription Wrap up
Project 34: Four in a Row
804
Setting up Creating the interface with UIStackView Preparing for basic play Adding in players: GKGameModelPlayer Detecting wins and draws in Four in a Row
www.hackingwithswift.com
8
How GameplayKit works: GKGameModel, GKGameModelPlayer and GKGameModelUpdate Implementing GKGameModel: gameModelUpdatesForPlayer() and applyGameModelUpdate() Creating a GameplayKit AI using GKMinmaxStrategist Wrap up
Project 35: Random Numbers
848
Setting up Generating random numbers in iOS 8 and earlier Generating random numbers with GameplayKit: GKRandomSource Choosing a random number source: GKARC4RandomSource and other GameplayKit options Shaping GameplayKit random numbers: GKRandomDistribution, GKShuffledDistribution and GKGaussianDistribution Shuffling an array with GameplayKit: arrayByShufflingObjectsInArray() Wrap up
Project 36: Crashy Plane
867
Setting up Creating a player: ResizeFill vs AspectFill Sky, background and ground: parallax scrolling with SpriteKit Creating collisions and making random numbers with GameplayKit Pixel-perfect physics in SpriteKit, plus explosions and more Background music with SKAudioNode, an intro, plus game over Wrap up
Project 37: Psychic Tester
901
Setting up Laying out the cards: addChildViewController() Animating a 3D flip effect using transitionWithView() Adding a CAGradientLayer with IBDesignable and IBInspectable Creating a particle system using CAEmitterLayer Wiggling cards and background music with AVAudioPlayer How to measure touch strength using 3D Touch Communicating between iOS and watchOS: WCSession Designing a simple watchOS app to receive data Wrap up
Project 38: GitHub Commits
941
Setting up Designing a Core Data model Adding Core Data to our project: NSPersistentStoreCoordinator
www.hackingwithswift.com
9
Creating an NSManagedObject subclass with Xcode Loading Core Data objects using NSFetchRequest and NSSortDescriptor How to make a Core Data attribute unique using constraints Examples of using NSPredicate to filter NSFetchRequest Adding Core Data entity relationships: lightweight vs heavyweight migration How to delete a Core Data object Optimizing Core Data Performance using NSFetchedResultsController Wrap up
Project 39: Unit testing with XCTest
998
Setting up Creating our first unit test using XCTest Loading our data and splitting up words: filter() Counting unique strings in an array measureBlock(): How to optimize our slow code and adjust the baseline Filtering using functions as parameters Updating the user interface with filtering User interface testing with XCTest Wrap up
Appendix: The Swift Knowledge Base
www.hackingwithswift.com
1043
10
About this book The Hacking with Swift tutorial series is designed to make it easy for beginners to get started coding for iPad and iPhone using the Swift programming language. My teaching method skips out a lot of theory. It skips out the smart techniques that transform 20 lines of easy-to-understand code into 1 line of near-magic. It ignores coding conventions by the dozen. And perhaps later on, once you've finished, you'll want to go back and learn all the theory I so blithely walked past. But let me tell you this: the problem with learning theory by itself is that your brain doesn't really have any interest in remembering stuff just for the sake of it. You see, here you'll be learning to code on a Need To Know basis. Nearly everything you learn from me will have a direct, practical application to something we're working on. That way, your brain can see exactly why a certain technique is helpful and you can start using it straight away. This book has been written on the back of my personal motto: "Programming is an art. Don't spend all your time sharpening your pencil when you should be drawing." We'll be doing some "sharpening" but a heck of a lot more "drawing" – if that doesn't suit your way of learning, you should exit now.
The three golden rules The series is crafted around a few basic tenets, and it's important you understand them before continuing: 1. Follow the series: The tutorials are designed to be used in order, starting at the beginning and working through to the end. The reason for this is that concepts are introduced sequentially on a need-to-know basis – you only learn about something when you really have to in order to make the project work. 2. Don't skip the games and techniques: The tutorials follow a sequence: app, game, technique, app, game, technique, etc. That is, you develop an app, then you develop a game, then we focus on a particular iOS component together to help make your apps better. The apps and games are, of course, standalone projects that you can go on to develop as
www.hackingwithswift.com
11
you wish, whereas the technique tutorials will often be used to improve or prepare you for other projects. 3. Get ready to hack: This is not designed to be the one-stop learning solution for all your Swift needs. It's called "Hacking with Swift" because the goal of each project is to reach the end with as little complication as possible – we're hacking, or playing around, with the language, not trying to give you Comp Sci 101. I can't re-iterate that last point enough. What I have found time and time again is that any tutorial, no matter how carefully written or what audience it's aimed at, will fail to fit the needs of many possible readers. And these people get angry, saying how the tutorial is wrong, how the tutorial is lame, how their tutorial would be much better if only they had the time to write it, and so on. Over the last 12 years of writing, I have learned to ignore minority whinging and move on, because what matters is that this tutorial is useful to you. You'd be surprised by how many people think the path to success is through reading books, attending classes or, well, doing pretty much anything except sitting down in front of a computer and typing. Not me. I believe the best way to learn something is try to it yourself and see how it goes. Sure, going to classes might re-enforce what you've learned, or it might teach you some time-saving techniques, but ultimately I've met too many people with computing degrees who stumble when asked to write simple programs. Don't believe me? Try doing a Google search for "fizz buzz test", and you'll be surprised too. So, dive in, make things, and please, please, please have fun – because if you're not enjoying yourself, Swift coding probably isn't for you. To download the files for any Hacking with Swift project, or if you'd like to learn more about the series, visit the series website and see the full range: hackingwithswift.com. If you spot any errors in this book, either typos or technical mistakes, please do let me know so I can correct them as soon as possible. The best way to get in touch is on Twitter @twostraws, but you can also email [email protected]
www.hackingwithswift.com
12
Xcode, Swift and iOS I'm not going to talk much, because I want to get straight into coding. However, there are some points you do need to know: • You should install the latest Xcode from the Mac App Store. It's free, and includes everything you need to make iOS apps in the iOS Simulator. Most of the projects in this series will be developed in the simulator, but a couple will require a device because the technology isn't available in the simulator – things like Touch ID and the accelerometer, for example. Projects that require a device also require you to have an active iOS developer account with Apple so that you can deploy your project to a device. • Swift is a relatively new language, and is evolving quickly. Every new release of Xcode seems to change something or other, and often that means code that used to work now no longer does. At the time of writing, Swift is mature enough that the changes are relatively minor, so hopefully you can make them yourself. If not, check to see if there's an update of the project files on hackingwithswift.com. • These projects are designed to work with iOS 9.0 or later, which is the version that runs on the majority of devices. You can downgrade them to 7.0 with relatively few changes if you desperately want to maximise your reach, but it's really not worth it at this point. Important note: if any bugs are found in the project files, or if Swift updates come out that force syntax changes, I'm going to be making changes to the projects on the website and updating this book as needed. Please make sure you read the release notes for each project to see what's changed, and follow me on Twitter @twostraws if you want to be notified of updates. I'm also happy to answer questions on Twitter if you encounter problems, so please feel free to get in touch! Swift, the Swift logo, Xcode, Instruments, Cocoa Touch, Touch ID, AirDrop, iBeacon, iPhone, iPad, Safari, App Store, Mac and OS X are trademarks of Apple Inc., registered in the U.S. and other countries. Hacking with Swift is copyright Paul Hudson. All rights reserved. No part of this book or corresponding materials (such as text, images, or source code) may be reproduced or distributed by any means without prior written permission of the copyright owner.
www.hackingwithswift.com
13
Dedication This book is dedicated to my daughter Charlotte, aka "Bonk", who has provided lots of hugs and lots of happiness at every point in its creation.
www.hackingwithswift.com
14
Introduction Swift for Complete Beginners If you want to learn the language all at once before you start making apps, this is for you.
www.hackingwithswift.com
15
How to install Xcode and create a playground Xcode is Apple's programming application for developers. It's free from the Mac App Store, and it's required to do iPhone and iPad development. So, your first action is to click here to install Xcode from the Mac App Store – it's quite a big download, so start downloading it now and carry on reading. While that's downloading, I can explain a couple of the absolute basics to you: • iOS is the name of the operating system that runs on all iPhones and iPads. It's responsible for all the basic operations of the phone, such as making phone calls, drawing on the screen, and running apps. • Swift is Apple's modern programming language that lets you write apps for iOS. It contains the functionality for building programs, but doesn't handle anything like user interfaces, audio or networking. • Swift 1.2 was the first major update to Swift, tweaking various language features and improving others. • Swift 2 is the second major update to Swift, and it's the version used across all of Hacking with Swift. • UIKit is Apple's user interface toolkit. It contains things like buttons, text boxes, navigation controls and more, and you drive it using Swift. • Cocoa Touch is the name given for Apple's vast collection of frameworks for iOS. It includes UIKit to do user interfaces, but also SpriteKit for making 2D games, SceneKit for making 3D games, MapKit for maps, Core Graphics for drawing, Core Animation for animating things, iAd for placing adverts, and much more. • NeXTSTEP is an operating system created by a company that Steve Jobs founded called NeXT. It was bought by Apple, at which point Jobs was placed back in control of the company, and put NeXTSTEP technology right into the core of Apple's development platform. • iOS Simulator is a tool that comes with Xcode that looks and works almost exactly like a real iPhone or iPad. It lets you test your app very quickly without having to use a real device. • Playgrounds are miniature Swift testing environments that let you type code and see the results immediately. You don't build real apps with them, but they are great for learning. We'll be using playgrounds in this introduction. • Crashes are when your code goes disastrously wrong and your app cannot recover. If a user is running your app it will just disappear and they'll be back on the home screen. If you're running in Xcode, you'll see a crash report.
www.hackingwithswift.com
16
• Taylor Swift has nothing to do with the Swift programming language. This is a shame, as you might imagine, but I'll try to make up for this shortfall by using her songs in this tutorial. Deal with it. That's it for the basics – if Xcode still hasn't finished downloading then why not watch some Taylor Swift videos while you wait? The examples in this tutorial will certainly make a lot more sense…
www.hackingwithswift.com
17
Variables and constants Every useful program needs to store data at some point, and in Swift there are two ways to do it: variables and constants. A variable is a data store that can have its value changed whenever you want, and a constant is a data store that you set once and can never change. So, variables have values that can vary, and constants have values that are constant – easy, right? Having both these options might seem pointless, after all you could just create a variable then never change it – why does it need to be made a constant? Well, it turns out that many programmers are – shock! – less than perfect at programming, and we make mistakes. One of the advantages of separating constants and variables is that Xcode will tell us if we've made a mistake. If we say, "make this date a constant, because I know it will never change" then 10 lines later try to change it, Xcode will refuse to build our app. Constants are also important because they let Xcode make decisions about the way it builds your app. If it knows a value will never change, it is able to apply optimizations to make your code run faster. In Swift, you make a variable using the var keyword, like this:
var name = "Tim McGraw"
Let's put that into a playground so you can start getting feedback. Delete everything in there apart from the import UIKit line (that's the bit that pulls in Apple's core iOS framework and it's need later on), and add that variable. You should see the picture below.
www.hackingwithswift.com
18
In Xcode playgrounds, you type your code on the left and see results on the right a second later.
Because this is a variable, you can change it whenever you want, but you shouldn't use the var keyword each time – that's only used when you're declaring new variables. Try writing this:
var name = "Tim McGraw" name = "Romeo"
So, the first line creates the name variable and gives it an initial value, then the second line updates the name variable so that its value is now "Romeo". You'll see both values printed in the results area of the playground. Now, what if we had made that a constant rather than a variable? Well, constants use the let keyword rather than var, so you can change your first line of code to say let name rather than var name like this:
import UIKit let name = "Tim McGraw" name = "Romeo"
But now there's a problem: Xcode is showing a red warning symbol next to line three, and it should have drawn a squiggly underline underneath name. If you click the red warning
www.hackingwithswift.com
19
should have drawn a squiggly underline underneath name. If you click the red warning symbol, Xcode will tell you the problem: "Cannot assign to 'let' value 'name'" – which is Xcode-speak for "you're trying to change a constant and you can't do that."
If you try to change a constant in Swift, Xcode will refuse to build your app.
So, constants are a great way to make a promise to Swift and to yourself that a value won't change, because if you do try to change it Xcode will refuse to run. Swift developers have a strong preference to use constants wherever possible because it makes your code easier to understand. In fact, in the very latest versions of Swift, Xcode will actually tell you if you make something a variable then never change it! Important note: variable and constant names must be unique in your code. You'll get an error if you try to use the same variable name twice, like this:
var name = "Tim McGraw" var name = "Romeo"
If the playground finds an error in your code, it will either flag up a warning in a red box, or will just refuse to run. You'll know if the latter has happened because the text in the results pane has gone gray rather than its usual black.
www.hackingwithswift.com
20
Types of Data There are lots of kinds of data, and Swift handles them all individually. You already saw one of the most important types when you assigned some text to a variable, but in Swift these are called a String – literally a string of characters. Strings can be long (e.g. a million letters or more), short (e.g. 10 letters) or even empty (no letters), it doesn't matter: they are all strings in Swift's eyes, and all work the same. Swift knows that name should hold a string because you assign a string to it when you create it: "Tim McGraw". If you were to rewrite your code to this it would stop working:
var name name = "Tim McGraw"
This time Xcode will give you an error message that won't make much sense just yet: "Type annotation missing in pattern". What it means is, "I can't figure out what data type name is because you aren't giving me enough information." At this point you have two options: either create your variable and give it an initial value on one line of code, or use what's called a type annotation, which is where you tell Swift what data type the variable will hold later on, even though you aren't giving it a value right now. You've already seen how the first option looks, so let's look at the second: type annotations. We know that name is going to be a string, so we can tell Swift that by writing a colon then String, like this:
var name: String name = "Tim McGraw"
You'll have no errors now, because Swift knows what type of data name will hold in the future. Note: some people like to put a space before and after the colon, making var name : String, but they are wrong and you should try to avoid mentioning their wrongness in polite
www.hackingwithswift.com
21
company. The lesson here is that Swift always wants to know what type of data every variable or constant will hold. Always. You can't escape it, and that's a good thing because it provides something called type safety – if you say "this will hold a string" then later try and put a rabbit in there, Swift will refuse. We can try this out now by introducing another important data type, called Int, which is short for "integer." Integers are round numbers like 3, 30, 300, or -16777216. For example:
var name: String name = "Tim McGraw"
var age: Int age = 25
That declares one variable to be a string and one to be an integer. Note how both String and Int have capital letters at the start, whereas name and age do not – this is the standard coding convention in Swift. A coding convention is something that doesn't matter to Swift (you can write your names how you like!) but does matter to other developers. In this case, data types start with a capital letter, whereas variables and constants do not. Now that we have variables of two different types, you can see type safety in action. Try writing this:
name = 25 age = "Time McGraw"
In that code, you're trying to put an integer into a string variable, and a string into an integer variable – and, thankfully, Xcode will throw up errors. You might think this is pedantic, but it's actually quite helpful: you make a promise that a variable will hold one particular type of data, and Xcode will enforce that throughout your work. Before you go on, please delete those two lines of code causing the error, otherwise
www.hackingwithswift.com
22
Before you go on, please delete those two lines of code causing the error, otherwise nothing in your playground will work going forward!
Float and Double Let's look at two more data types, called Float and Double. This is Swift's way of storing numbers with a fractional component, such as 3.1, 3.141, 3.1415926, and -16777216.5. There are two data types for this because you get to choose how much accuracy you want, but most of the time it doesn't matter so the official Apple recommendation is always to use Double because it has the highest accuracy. Try putting this into your playground:
var latitude: Double latitude = 36.166667
var longitude: Float longitude = -86.783333
You can see both numbers appear on the right, but look carefully because there's a tiny discrepancy. We said that longitude should be equal to -86.783333, but in the results pane you'll see -86.78333 – it's missing one last 3 on the end. Now, you might well say, "what does 0.000003 matter amongst friends?" but this is ably demonstrating what I was saying about accuracy. Because these playgrounds update as you type, we can try things out so you can see exactly how Float and Double differ. Try changing the code to be this:
var longitude: Float longitude = -86.783333 longitude = -186.783333 longitude = -1286.783333 longitude = -12386.783333 longitude = -123486.783333
www.hackingwithswift.com
23
longitude = -123486.783333 longitude = -1234586.783333
That's adding increasing numbers before the decimal point, while keeping the same amount of numbers after. But if you look in the results pane you'll notice that as you add more numbers before the point, Swift is removing numbers after. This is because it has limited space in which to store your number, so it's storing the most important part first – being off by 1,000,000 is a big thing, whereas being off by 0.000003 is less so.
In Swift a Float holds much less data than a Double, so you should use Double where possible.
Now try changing the Float to be a Double and you'll see Swift prints the correct number out every time:
var longitude: Double
This is because, again, Double has twice the accuracy of Float so it doesn't need to cut your
www.hackingwithswift.com
24
number to fit. Doubles still have limits, though – if you were to try a massive number like 123456789.123456789 you would see it gets cut down to 123456789.1234568.
Boolean Swift has a built-in data type that can store whether a value is true or false, called a Bool, short for Boolean. Bools don't have space for "maybe" or "perhaps", only absolutes: true or false. For example:
var stayOutTooLate: Bool stayOutTooLate = true
var nothingInBrain: Bool nothingInBrain = true
var missABeat: Bool missABeat = false
Using type annotations wisely As you've learned, there are two ways to tell Swift what type of data a variable holds: assign a value when you create the variable, or use a type annotation. If you have a choice, the first is always preferable because it's clearer. For example:
var name = "Tim McGraw"
www.hackingwithswift.com
25
…is preferred to:
var name: String name = "Tim McGraw"
This applies to all data types. For example:
var age = 25 var longitude = -86.783333 var nothingInBrain = true
This technique is called type inference, because Swift can infer what data type should be used for a variable by looking at the type of data you want to put in there. When it comes to numbers like -86.783333, Swift will always infer a Double rather than a Float. For the sake of completeness, I should add that it's possible to specify a data type and provide a value at the same time, like this:
var name: String = "Tim McGraw"
www.hackingwithswift.com
26
Operators Operators are those little symbols you learned in your very first math classes: + to add, - to subtract, * to multiply, / to divide, = to assign value, and so on. They all exist in Swift, along with a few extras. Let's try a few basics – please type this into your playground:
var a = 10 a = a + 1 a = a - 1 a = a * a
In the results pane, you'll see 10, 11, 10 and 100 respectively. Now try this:
var b = 10 b += 10 b -= 10
+= is an operator that means "add then assign to." In our case it means "take the current value of b, add 10 to it, then put the result back into b." As you might imagine, -= does the same but subtracts rather than adds. So, that code will show 10, 20, 10 in the results pane. Some of these operators apply to other data types. As you might imagine, you can add two doubles together like this:
var a = 1.1 var b = 2.2 var c = a + b
www.hackingwithswift.com
27
When it comes to strings, + will join them together. For example:
var name1 = "Tim McGraw" var name2 = "Romeo" var both = name1 + " and " + name2
That will write "Tim McGraw and Romeo" into the results pane.
Comparison operators Swift has a set of operators that perform comparisons on values. For example:
var a = 1.1 var b = 2.2 var c = a + b
c > 3 c >= 3 c > 4 c < 4
That shows off greater than (>), greater than or equal (>=), and less than ( String? { return "Hate" }
Note the extra question mark: that means "optional string." Now, in our case we're still returning "Hate" no matter what, but let's go ahead and modify that function further: if the weather is sunny, the haters have turned over a new leaf and have given up their life of
www.hackingwithswift.com
54
hating, so want to return no value. In Swift, this "no value" has a special name: nil. Change the function to this:
func getHaterStatus(weather: String) -> String? { if weather == "sunny" { return nil } else { return "Hate" } }
That accepts one string parameter (the weather) and returns one string (hating status), but that return value might be there or it might not – it's nil. In this case, it means we might get a string, or we might get nil. Now for the important stuff: Swift wants your code to be really safe, and trying to use a nil value is a bad idea – it might crash your code, it might screw up your app logic, or it might make your user interface show the wrong thing. As a result, when you declare a value as being optional, Swift will make sure you handle it safely. Let's try this now: add these lines of code to your playground:
var status: String status = getHaterStatus("rainy")
The first line creates a string variable, and the second assigns to it the value from getHaterStatus() – and today the weather is rainy, so those haters are hating for sure. That code will not run, because we said that status is of type String, which requires a value, but getHaterStatus() might not provide one because it returns an optional string. Swift simply will not let you make this mistake, which is extremely helpful because it effectively
www.hackingwithswift.com
55
stops dead a whole class of common bugs. To fix the problem, we need to make the status variable a String?, or just remove the type annotation entirely and let Swift use type inference. The first option looks like this:
var status: String? status = getHaterStatus("rainy")
And the second like this:
var status = getHaterStatus("rainy")
Regardless of which you choose, that value might be there or might not, and by default Swift won't let you use it dangerously. As an example, imagine a function like this:
func takeHaterAction(status: String) { if status == "Hate" { print("Hating") } }
That takes a string and prints a message depending on its contents. This function takes a String value, and not a String? value – you can't pass in an optional here, it wants a real string, which means we can't call it using the status variable. Swift has two solutions. Both are used, but one is definitely preferred over the other. The first solution is called optional unwrapping, and it's done inside a conditional statement using special syntax. It does two things at the same time: checks whether an optional has a value, and if so unwraps it into a non-optional type then runs a code block. The syntax looks like this:
www.hackingwithswift.com
56
The syntax looks like this:
if let unwrappedStatus = status { // unwrappedStatus contains a non-optional value! } else { // in case you you want an else block, here you go… }
These if let statements check and unwrap in one succinct line of code, which makes them very common. Using this method, we can safely unwrap the return value of getHaterStatus() and be sure that we only call takeHaterAction() with a valid, non-optional string. Here's the complete code:
func getHaterStatus(weather: String) -> String? { if weather == "sunny" { return nil } else { return "Hate" } }
func takeHaterAction(status: String) { if status == "Hate" { print("Hating") } }
if let status = getHaterStatus("rainy") { takeHaterAction(status) }
www.hackingwithswift.com
57
If you understand this concept, you're welcome to skip down to the title that says "Force unwrapping optionals". If you're still not quite sure about optionals, carry on reading.OK, if you're still here it means the explanation above either made no sense, or you sort of understood but could probably use some clarification. Optionals are used extensively in Swift, so you really do need to understand them. I'm going to try explaining again, in a different way, and hopefully that will help! Here's a new function:
func yearAlbumReleased(name: String) -> Int { if name == "Taylor Swift" { return 2006 } if name == "Fearless" { return 2008 } if name == "Speak Now" { return 2010 } if name == "Red" { return 2012 } if name == "1989" { return 2014 }
return 0 }
That takes the name of a Taylor Swift album, and returns the year it was released. But if we call it with the album name "Lantern" because we mixed up Taylor Swift with Hudson Mohawke (an easy mistake to make, right?) then it returns 0 because it's not one of Taylor's albums. But does 0 make sense here? Sure, if the album was released back in 0 AD when Caesar Augustus was emperor of Rome, 0 might make sense, but here it's just confusing – people need to know that 0 means "not recognized." A much better idea is to re-write that function so that it either returns an integer (when a year was found) or nil (when nothing was found), which is easy thanks to optionals. Here's the new function:
func yearAlbumReleased(name: String) -> Int? { if name == "Taylor Swift" { return 2006 } if name == "Fearless" { return 2008 }
www.hackingwithswift.com
58
if name == "Fearless" { return 2008 } if name == "Speak Now" { return 2010 } if name == "Red" { return 2012 } if name == "1989" { return 2014 }
return nil }
Now that it returns nil, we need to unwrap the result using if let because we need to check whether a value exists or not. If you understand this concept, you're welcome to skip down to the title that says "Implict and force unwrapping optionals". If you're still not quite sure about optionals, carry on reading.OK, if you're still here it means you're really struggling with optionals, so I'm going to have one last go at explaining them. Here's an array of names:
var items = ["James", "John", "Sally"]
If we wanted to write a function that looked in that array and told us the index of a particular name, we might write something like this:
func positionOfString(items: [String], str: String) -> Int { for i in 0 ..< items.count { if items[i] == str { return i } }
return 0
www.hackingwithswift.com
59
return 0 }
That loops through all the items in the array, returning its position if it finds a match, otherwise returning 0. Now try running these three lines of code:
let jamesPosition = positionOfString(items, str: "James") let johnPosition = positionOfString(items, str: "John") let sallyPosition = positionOfString(items, str: "Sally") let bobPosition = positionOfString(items, str: "Bob")
That will output 0, 1, 2, 0 – the positions of James and Bob are the same, even though one exists and one doesn't. This is because I used 0 to mean "not found." The easy fix might be to make -1 not found, but whether it's 0 or -1 you still have a problem because you have to remember that specific number means "not found." The solution is optionals: return an integer if you found the match, or nil otherwise. In fact, this is exactly the approach the built-in "find in array" functions work: someArray.indexOf(someValue). When you work with these "might be there, might not be" values, Swift forces you to unwrap them before using them, thus acknowledging that there might not be a value. That's what if let syntax does: if the optional has a value then unwrap it and use it, otherwise don't use it at all. If you're still not sure how optionals work, then the best thing to do is ask me on Twitter and I'll try to help: you can find me @twostraws.
Force unwrapping optionals
www.hackingwithswift.com
60
Swift lets you override its safety by using the exclamation mark character: !. If you know that an optional definitely has a value, you can force unwrap it by placing this exclamation mark after it. Please be careful, though: if you try this on a variable that does not have a value, your code will crash. To put together a working example, here's some foundation code:
func yearAlbumReleased(name: String) -> Int? { if name == "Taylor Swift" { return 2006 } if name == "Fearless" { return 2008 } if name == "Speak Now" { return 2010 } if name == "Red" { return 2012 } if name == "1989" { return 2014 }
return nil }
var year = yearAlbumReleased("Red")
if year == nil { print("There was an error") } else { print("It was released in \(year)") }
That gets the year an album was released. If the album couldn't be found, year will be set to nil, and an error message will be printed. Otherwise, the year will be printed. Or will it? Well, yearAlbumReleased() returns an optional string, and this code doesn't use if let to unwrap that optional. As a result, it will print out the following: "It was released in Optional(2012)" – probably not what we wanted!
www.hackingwithswift.com
61
At this point in the code, we have already checked that we have a valid value, so it's a bit pointless to have another if let in there to safely unwrap the optional. So, Swift provides a solution – change the second print() call to this:
print("It was released in \(year!)")
Note the exclamation mark: it means "I'm certain this contains a value, so force unwrap it now."
Implicitly unwrapped optionals You can also use this exclamation mark syntax to create implicitly unwrapped optionals, which is where some people really start to get confused. So, please read this carefully! • A regular variable must contain a value. Example: String must contain a string, even if that is string empty, i.e. "". • An optional variable might contain a value, or might not. It must be unwrapped before it is used. Example: String? might contain a string, or it might contain nil. The only way to find out is to unwrap it. • An implicitly unwrapped optional might contain a value, or might not. But it does not need to be unwrapped before it is used. Swift won't check for you, so you need to be extra careful. Example: String! might contain a string, or it might contain nil – and it's down to you to use it appropriately. There are two main times you're going to meet implicitly unwrapped optionals. The first is when you're working with Apple's APIs: these frequently return implicitly unwrapped optionals because their code pre-dates Swift and that was how things were done in Ye Olde Ages Of Programming. The second is when you're working with user interface elements in UIKit. These need to be declared up front, but you can't use them until they have been created by iOS – and it likes to create user interface elements at the last possible moment to avoid any unnecessary work. Having to continually unwrap values you definitely know will be there is annoying, so these are made implicitly unwrapped.
www.hackingwithswift.com
62
Don't worry if you find implicitly unwrapped optionals a bit hard to grasp - it will become clear as you work with the language.
www.hackingwithswift.com
63
Optional chaining Working with optionals can feel a bit clumsy sometimes, and all the unwrapping and checking can become so onerous that you might be tempted to throw some exclamation marks to force unwrap stuff so you can get on with work. Be careful, though: if you force unwrap an optional that doesn't have a value, your code will crash. Swift has two techniques to help make your code less complicated. The first is called optional chaining, which lets you run code only if your optional has a value. Put the below code into your playground to get us started:
func albumReleasedYear(year: Int) -> String? { switch year { case 2006: return "Taylor Swift" case 2008: return "Fearless" case 2010: return "Speak Now" case 2012: return "Red" case 2014: return "1989" default: return nil } }
let album = albumReleasedYear(2006) print("The album is \(album)")
That will output "The album is Optional("Taylor Swift")" into the results pane. If we wanted to convert the return value of albumReleasedYear() to be uppercase letters (that is, "TAYLOR SWIFT" rather than "Taylor Swift") we could use the uppercaseString value of that string. For example:
let str = "Hello world" print(str.uppercaseString)
www.hackingwithswift.com
64
print(str.uppercaseString)
The problem is, albumReleasedYear() returns an optional string: it might return a string or it might return nothing at all. So, what we really mean is, "if we got a string back make it uppercase, otherwise do nothing." And that's where optional chaining comes in, because it provides exactly that behavior. Try changing the last two lines of code to this:
let album = albumReleasedYear(2006)?.uppercaseString print("The album is \(album)")
Note that there's a question mark in there, which is the optional chaining: everything after the question mark will only be run if everything before the question mark has a value. This doesn't affect the underlying data type of album, because that line of code will now either return nil or will return the uppercase album name – it's still an optional string. Your optional chains can be as long as you need, for example:
let album = albumReleasedYear(2006)?.someOptionalValue?.someOtherOptionalValue?.w hatever
Swift will check them from left to right until it finds nil, at which point it stops.
The nil coalescing operator This simple Swift feature makes your code much simpler and safer, and yet has such a grandiose name that many people are scared of it. This is a shame, because the nil coalescing operator will make your life easier if you take the time to figure it out!
www.hackingwithswift.com
65
What it is does is let you say "use value A if you can, but if value A is nil then use value B." That's it. It's particularly helpful with optionals, because it effectively stops them from being optional because you provide a non-optional value B. So, if A is optional and has a value, it gets used (we have a value.) If A is present and has no value, B gets used (so we still have a value). Either way, we definitely have a value. To give you a real context, try using this code in your playground:
let album = albumReleasedYear(2006) ?? "unknown" print("The album is \(album)")
That double question mark is the nil coalescing operator, and in this situation it means "if albumReleasedYear() returned a value then put it into the album variable, but if albumReleasedYear() returned nil then use 'unknown' instead." If you look in the results pane now, you'll see "The album is Taylor Swift" printed in there – no more optionals. This is because Swift can now be sure it will get a real value back, either because the function returned one or because you're providing "unknown". This in turn means you don't need to unwrap anything or risk crashes – you're guaranteed to have real data to work with, which makes your code safer and easier to work with.
www.hackingwithswift.com
66
Enumerations Enumerations – usually just called "enum" and pronounced "ee-num" - are a way for you to define your own kind of value in Swift. In some programming languages they are simple little things, but Swift adds a huge amount of power to them if you want to go beyond the basics. Let's start with a simple example from earlier:
func getHaterStatus(weather: String) -> String? { if weather == "sunny" { return nil } else { return "Hate" } }
That function accepts a string that defines the current weather. The problem is, a string is a poor choice for that kind of data – is it "rain", "rainy" or "raining"? Or perhaps "showering", "drizzly" or "stormy"? Worse, what if one person writes "Rain" with an uppercase R and someone else writes "Ran" because they weren't looking at what they typed? Enums solve this problem by letting you define a new data type, then define the possible values it can hold. For example, we might say there are five kinds of weather: sun, cloud, rain, wind and snow. If we make this an enum, it means Swift will accept only those five values – anything else will trigger an error. And behind the scenes enums are usually just simple numbers, which are a lot faster than strings for computers to work with. Let's put that into code:
enum WeatherType { case Sun, Cloud, Rain, Wind, Snow }
func getHaterStatus(weather: WeatherType) -> String? {
www.hackingwithswift.com
67
func getHaterStatus(weather: WeatherType) -> String? { if weather == WeatherType.Sun { return nil } else { return "Hate" } }
getHaterStatus(WeatherType.Cloud)
Take a look at the first three lines: line 1 gives our type a name, WeatherType. This is what you'll use in place of String or Int in your code. Line 2 defines the five possible cases our enum can be, as I already outlined. Convention has these start with a capital letter, so Sun, Cloud, etc. And line 3 is just a closing brace, ending the enum. Now take a look at how it's used: I modified the getHaterStatus() so that it takes a WeatherType value. The conditional statement is also rewritten to compare against WeatherType.Sun, which is our value. Remember, this check is just a number behind the scenes, which is lightning fast. Now, go back and read that code again, because I'm about to rewrite it with two changes that are important. All set?
enum WeatherType { case Sun case Cloud case Rain case Wind case Snow }
func getHaterStatus(weather: WeatherType) -> String? { if weather == .Sun {
www.hackingwithswift.com
68
if weather == .Sun { return nil } else { return "Hate" } }
getHaterStatus(.Cloud)
I made two differences there. First, each of the weather types are now on their own line. This might seem like a small change, and indeed in this example it is, but it becomes important soon. The second change was that I wrote if weather == .Sun – I didn't need to spell out that I meant WeatherType.Sun because Swift knows I am comparing against a WeatherType variable, so it's using type inference. Note that at this time Xcode is unable to use code completion to suggest enums if you use this short form. If you type them in full, e.g. WeatherType.Sun, you will get code completion. Enums are particularly useful inside switch/case blocks, particularly because Swift knows all the values your enum can have so it can ensure you cover them all. For example, we might try to rewrite the getHaterStatus() method to this:
func getHaterStatus(weather: WeatherType) -> String? { switch weather { case .Sun: return nil case .Cloud, .Wind: return "dislike" case .Rain: return "hate" } }
www.hackingwithswift.com
69
Yes, I realise "haters gonna dislike" is hardly a great lyric, but its academic anyway because this code won't build: it doesn't handle the .Snow case, and Swift wants all cases to be covered. You either have to add a case for it or add a default case.
Enums with additional values One of the most powerful features of Swift is that enumerations can have values attached to them that you define. To extend our increasingly dubious example a bit further, I'm going to add a value to the .Wind case so that we can say how fast the wind is. Modify your code to this:
enum WeatherType { case Sun case Cloud case Rain case Wind(speed: Int) case Snow }
As you can see, the other cases don't need a speed value – I put that just into wind. Now for the real magic: Swift lets us add extra conditions to the swift/case block so that a case will match only if those conditions are true. This uses the let keyword to access the value inside a case, then the where keyword for pattern matching. Here's the new function:
func getHaterStatus(weather: WeatherType) -> String? { switch weather { case .Sun:
www.hackingwithswift.com
70
case .Sun: return nil case .Wind(let speed) where speed < 10: return "meh" case .Cloud, .Wind: return "dislike" case .Rain, .Snow: return "hate" } }
getHaterStatus(WeatherType.Wind(speed: 5))
You can see .Wind appears in there twice, but the first time is true only if the wind is slower than 10 kilometers per hour. If the wind is 10 or above, that won't match. The key is that you use let to get hold of the value inside the enum (i.e. to declare a constant name you can reference) then use a where condition to check. Swift evaluates switch/case from top to bottom, and stops as soon as it finds match. This means that if case .Cloud, .Wind: appears before case .Wind(let speed) where speed < 10: then it will be executed instead – and the output changes. So, think carefully about how you order cases!
www.hackingwithswift.com
71
Structs Structs are complex data types, meaning that they are made up of multiple values. You then create an instance of the struct and fill in its values, then you can pass it around as a single value in your code. For example, we could define a Person struct type that contains two properties: clothes and shoes:
struct Person { var clothes: String var shoes: String }
When you define a struct, Swift makes them very easy to create because it automatically generates what's called a memberwise initializer. In plain speak, it means you create the struct by passing in initial values for its two properties, like this:
let taylor = Person(clothes: "T-shirts", shoes: "sneakers") let other = Person(clothes: "short skirts", shoes: "high heels")
Once you have created an instance of a struct, you can read its properties just by writing the name of the struct, a period, then the property you want to read:
print(taylor.clothes) print(other.shoes)
If you assign one struct to another, Swift copies it behind the scenes so that it is a complete, standalone duplicate of the original. Well, that's not strictly true: Swift uses a technique called "copy on write" which means it only actually copies your data if you try to change it. To help you see how struct copies work, put this into your playground:
www.hackingwithswift.com
72
struct Person { var clothes: String var shoes: String }
let taylor = Person(clothes: "T-shirts", shoes: "sneakers") let other = Person(clothes: "short skirts", shoes: "high heels")
var taylorCopy = taylor taylorCopy.shoes = "flip flops"
taylor taylorCopy
That creates two Person structs, then creates a third one called taylorCopy as a copy of taylor. What happens next is the interesting part: the code changes taylorCopy, and prints both it and taylor. If you look in your results pane (you might need to resize it to fit) you'll see that the copy has a different value to the original: changing one did not change the other.
www.hackingwithswift.com
73
Classes Swift has another way of build complex data types called classes. They look similar to structs, but have a number of important differences, including: • You don't get an automatic memberwise initializer for your classes; you need to write your own. • You can define a class as being based off another class, adding any new things you want. • If you copy an object, both copies point at the same data by default. All three of those are massive differences, so I'm going to cover them in more depth before continuing.
Initializing an object If we were to convert our Person struct into a Person class, Swift wouldn't let us write this:
class Person { var clothes: String var shoes: String }
This is because we're declaring the two properties to be String, which if you remember means they absolutely must have a value. This was fine in a struct because Swift automatically produces a memberwise initializer for us that forced us to provide values for the two properties, but this doesn't happen with classes so Swift can't be sure they will be given values. There are three solutions: make the two values optional strings, give them default values, or write our own initializer. The first option is clumsy because it introduces optionals all over our code where they don't need to be. The second option works, but it's a bit wasteful unless those default values will actually be used. That leaves the third option, and really it's the right one: write our own initializer.
www.hackingwithswift.com
74
To do this, create a function inside the class called init() that takes the two parameters we care about:
class Person { var clothes: String var shoes: String
init(clothes: String, shoes: String) { self.clothes = clothes self.shoes = shoes } }
There are two things that might jump out at you in that code. First, you don't write func before your init() function, because it's special. Second, because the parameter names being passed in are the same as the names of the properties we want to assign, you use self. to make your meaning clear – "the clothes property of this object should be set to the clothes parameter that was passed in." You can give them unique names if you want – it's down to you. There are two more things you ought to know but can't see in that code. First, when you write a function inside a class, it's called a method instead. In Swift you write func whether it's a function or a method, but the distinction is preserved when you talk about them. Second, Swift requires that all non-optional properties have a value by the end of the initializer, or by the time the initializer calls any other method – whichever comes first.
Class inheritance The second difference between classes and structs are that classes can build on each other to produce greater things, known as class inheritance. This is a technique used extensively in
www.hackingwithswift.com
75
to produce greater things, known as class inheritance. This is a technique used extensively in Cocoa Touch, even in the most basic programs, so it's something you should get to grips with. Let's start with something simple: a Singer class that has properties, which is their name and age. As for methods, there will a simple initializer to handle setting the properties, plus a sing() method that outputs some words:
class Singer { var name: String var age: Int
init(name: String, age: Int) { self.name = name self.age = age }
func sing() { print("La la la la") } }
We can now create an instance of that object by calling that initializer, then read out its properties and call its method:
var taylor = Singer(name: "Taylor", age: 25) taylor.name taylor.age taylor.sing()
That's our basic class, but we're going to build on it: I want to define a CountrySinger class
www.hackingwithswift.com
76
that has everything the Singer class does, but when I call sing() on it I want to print "Trucks, girls, and liquor" instead. You could of course just copy and paste the original Singer into a new class called CountrySinger but that's a lazy way to program and it will come back to haunt if you make later changes to Singer and forget to copy them across. Instead, Swift has a smarter solution: we can define CountrySinger as being based off Singer and it will get all its properties and methods for us to build on:
class CountrySinger: Singer {
}
That colon is what does the magic: it means "CountrySinger extends Singer." Now, that new CountrySinger class (called a subclass) doesn't add anything to Singer (called the parent class, or superclass) yet. We want it to have its own sing() method, but in Swift you need to learn a new keyword: override. This means "I know this method was implemented by my parent class, but I want to change it for this subclass." Having the override keyword is helpful, because it makes your intent clear. It also allows Swift to check your code: if you don't use override Swift won't let you change a method you got from your superclass, or if you use override and there wasn't anything to override, Swift will point out your error. So, we need to use override func, like this:
class CountrySinger : Singer { override func sing() { print("Trucks, girls, and liquor") } }
www.hackingwithswift.com
77
Now modify the way the taylor object is created:
var taylor = CountrySinger(name: "Taylor", age: 25) taylor.sing()
If you change CountrySinger to just Singer you should be able to see the different messages appearing in the results pane. Now, to make things more complicated, we're going to define a new class called HeavyMetalSinger. But this time we're going to store a new property called noiseLevel defining how loud this particular heavy metal singer likes to scream down their microphone. This causes a problem, and it's one that needs to be solved in a very particular way: • Swift wants all non-optional properties to have a value. • Our Singer class doesn't have a noiseLevel property. • So, we need to create a custom initializer for HeavyMetalSinger that accepts a noise level. • That new initializer also needs to know the name and age of the heavy metal singer, so it can pass it onto the superclass Singer. • Passing on data to the superclass is done through a method call, and you can't make method calls in initializers until you have given all your properties values. • So, we need to set our own property first (noiseLevel) then pass on the other parameters for the superclass to use. That might sound awfully complicated, but in code it's straightforward. Here's the HeavyMetalSinger class, complete with its own sing() method:
class HeavyMetalSinger : Singer { var noiseLevel: Int
init(name: String, age: Int, noiseLevel: Int) { self.noiseLevel = noiseLevel super.init(name: name, age: age) }
www.hackingwithswift.com
78
}
override func sing() { print("Grrrrr rargh rargh rarrrrgh!") } }
Notice how its initializer takes three parameters, then calls super.init() to pass name and age on to the Singer superclass - but only after its own property has been set. You'll see super used a lot when working with objects, and it just means "call a method on the class I inherited from. It's usually used to mean "let my parent class do everything it needs to do first, then I'll do my extra bits." Class inheritance is a big topic so don't fret if it's not clear just yet. However, there is one more thing you need to know: class inheritance often spans many levels. For example, A could inherit from B, and B could inherit from C, and C could inherit from D, and so on. This lets you build functionality and re-use up over a number of classes, helping to keep your code modular and easy to understand.
Values vs References When you copy a struct, the whole thing is duplicated, including all its values. This means that changing one copy of a struct doesn't change the other copies – they are all individual. With classes, each copy of an object points at the same original object, so if you change one they all change. Swift calls structs "value types" because they just point at a value, and classes "reference types" because objects are just shared references to the real value. This is an important difference, and it means the choice between structs and classes is an important one: • If you want to have one shared state that gets passed around and modified in place, you're looking for classes. You can pass them into functions or store them in arrays, modify them in there, and have that change reflected in the rest of your program.
www.hackingwithswift.com
79
• If you want to avoid shared state where one copy can't affect all the others, you're looking for structs. You can pass them into functions or store them in arrays, modify them in there, and they won't change wherever else they are referenced. If I were to summarise this key difference between structs and classes, I'd say this: classes offer more flexibility, whereas structs offer more safety. As a basic rule, you should always use structs until you have a reason to use classes.
www.hackingwithswift.com
80
Properties Structs and classes (collectively: "types") can have their own variables and constants, and these are called properties. These let you attach values to your types to represent them uniquely, but because types can also have methods you can have them behave according to their own data. Let's take a look at an example now:
struct Person { var clothes: String var shoes: String
func describe() { print("I like wearing \(clothes) with \(shoes)") } }
let taylor = Person(clothes: "T-shirts", shoes: "sneakers") let other = Person(clothes: "short skirts", shoes: "high heels") taylor.describe() other.describe()
As you can see, when you use a property inside a method it will automatically use the value that belongs to the same object.
Property observers Swift lets you add code to be run when a property is about to be changed or has been changed. This is frequently a good way to have a user interface update when a value
www.hackingwithswift.com
81
changes, for example. There are two kinds of property observer: willSet and didSet, and they are called before or after a property is changed. In willSet Swift provides your code with a special value called newValue that contains what the new property value is going to be, and in didSet you are given oldValue to represent the previous value. Let's attach two property observers to the clothes property of a Person struct:
struct Person { var clothes: String { willSet { updateUI("I'm changing from \(clothes) to \(newValue)") }
didSet { updateUI("I just changed from \(oldValue) to \(clothes)") } } }
func updateUI(msg: String) { print(msg) }
var taylor = Person(clothes: "T-shirts") taylor.clothes = "short skirts"
That will print out the messages "I'm changing from T-shirts to short skirts" and "I just changed from T-shirts to short skirts."
www.hackingwithswift.com
82
Computed properties It's possible to make properties that are actually code behind the scenes. We already used the uppercaseString property of strings, for example – that's something that gets calculated as needed, rather than every string always storing an uppercase version of itself. To make a computed property, place an open brace after your property then use either get or set to make an action happen at the appropriate time. For example, if we wanted to add a ageInDogYears property that automatically returned a person's age multiplied by seven, we'd do this:
struct Person { var age: Int
var ageInDogYears: Int { get { return age * 7 } } }
var fan = Person(age: 25) print(fan.ageInDogYears)
Computed properties are common in Apple's code, but less common in user code. Many programmers prefer to use methods because their behavior is clearer.
www.hackingwithswift.com
83
Static properties and methods Swift lets you create properties and methods that belong to a type, rather than to instances of a type. This is helpful for organizing your data meaningfully by storing shared data. Swift calls these shared properties "static properties", and you create one just by using the static keyword. Once that's done, you access the property by using the full name of the type. Here's a simple example:
struct TaylorFan { static var favoriteSong = "Shake it Off"
var name: String var age: Int }
let fan = TaylorFan(name: "James", age: 25) print(TaylorFan.favoriteSong)
So, a Taylor Swift fan has a name and age that belongs to them, but they all have the same favorite song. Because static methods belong to the class rather than to instances of a class, you can't use it to access any non-static properties from the class.
www.hackingwithswift.com
84
Access control This is an important feature and one you need to understand, but sadly it's one that doesn't work in Swift playgrounds so you'll just have to take my word for it. Access control lets you specify what data inside structs and classes should be exposed to the outside world, and you get to choose three modifiers: • Public: this means everyone can read and write the property. • Internal: this means only your Swift code can read and write the property. • Private: this means that only Swift code in the same file as the type can read and write the property. Most of the time you don't need to specify access control, but sometimes you'll want to explicitly set a property to be private because it stops others from accessing it directly. This is useful because your own methods can work with that property, but others can't, thus forcing them to go through your code to perform certain actions. To declare a property private, just do this:
class TaylorFan { private var name: String! }
Reminder: this does nothing on Swift playgrounds, because one playground is effectively one file and thus can read and write any data it likes.
www.hackingwithswift.com
85
Polymorphism and type casting Because classes can inherit from each other (e.g. CountrySinger can inherit from Singer) it means one class is effectively a superset of another: class B has all the things A has, with a few extras. This in turn means that you can treat B as type B or as type A, depending on your needs. Confused? Let's try some code:
class Album { var name: String
init(name: String) { self.name = name } }
class StudioAlbum: Album { var studio: String
init(name: String, studio: String) { self.studio = studio super.init(name: name) } }
class LiveAlbum: Album { var location: String
init(name: String, location: String) { self.location = location super.init(name: name)
www.hackingwithswift.com
86
super.init(name: name) } }
That defines three classes: albums, studio albums and live albums, with the latter two both inheriting from Album. Because any instance of LiveAlbum is inherited from Album it can be treated just as either Album or LiveAlbum – it's both at the same time. This is called "polymorphism," but it means you can write code like this:
var taylorSwift = StudioAlbum(name: "Taylor Swift", studio: "The Castles Studios") var fearless = StudioAlbum(name: "Speak Now", studio: "Aimeeland Studio") var iTunesLive = LiveAlbum(name: "iTunes Live from SoHo", location: "New York")
var allAlbums: [Album] = [taylorSwift, fearless, iTunesLive]
There we create an array that holds only albums, but put inside it two studio albums and a live album. This is perfectly fine in Swift because they are all descended from the Album class, so they share the same basic behavior. We can push this a step further to really demonstrate how polymorphism works. Let's add a getPerformance() method to all three classes:
class Album { var name: String
init(name: String) { self.name = name }
www.hackingwithswift.com
87
func getPerformance() -> String { return "The album \(name) sold lots" } }
class StudioAlbum: Album { var studio: String
init(name: String, studio: String) { self.studio = studio super.init(name: name) }
override func getPerformance() -> String { return "The studio album \(name) sold lots" } }
class LiveAlbum: Album { var location: String
init(name: String, location: String) { self.location = location super.init(name: name) }
override func getPerformance() -> String { return "The live album \(name) sold lots" } }
www.hackingwithswift.com
88
The getPerformance() method exists in the Album class, but both child classes override it. When we create an array that holds Albums, we're actually filling it with subclasses of albums: LiveAlbum and StudioAlbum. They go into the array just fine because they inherit from the Album class, but they never lose their original class. So, we could write code like this:
var taylorSwift = StudioAlbum(name: "Taylor Swift", studio: "The Castles Studios") var fearless = StudioAlbum(name: "Speak Now", studio: "Aimeeland Studio") var iTunesLive = LiveAlbum(name: "iTunes Live from SoHo", location: "New York")
var allAlbums: [Album] = [taylorSwift, fearless, iTunesLive]
for album in allAlbums { print(album.getPerformance()) }
That will automatically use the override version of getPerformance() depending on the subclass in question. That's polymorphism in action: an object can work as its class and its parent classes, all at the same time.
Converting types with type casting You will often find you have an object of a certain type, but really you know it's a different type. Sadly, if Swift doesn't know what you know, it won't build your code. So, there's a solution, and it's called type casting: converting an object of one type to another. Chances are you're struggling to think why this might be necessary, but I can give you a very simple example:
www.hackingwithswift.com
89
simple example:
for album in allAlbums { print(album.getPerformance()) }
That was our loop from a few minutes ago. The allAlbums array holds the type Album, but we know that really it's holding one of the subclasses: StudioAlbum or LiveAlbum. Swift doesn't know that, so if you try to write something like print(album.studio) it will refuse to build because only StudioAlbum objects have that property. Type casting in Swift comes in three forms, but most of the time you'll only meet two: as? and as!, known as optional downcasting and forced downcasting. The former means "I think this conversion might be true, but it might fail," and the second means "I know this conversion is true, and I'm happy for my app to crash if I'm wrong." When I say "conversion" I don't mean that the object literally gets transformed. Instead, it's just converting how Swift treats the object – you're telling Swift that an object it thought was type A is actually type E. The question and exclamation marks should give you a hint of what's going on, because this is very similar to optional territory. For example, if you write this:
for album in allAlbums { let studioAlbum = album as? StudioAlbum }
Swift will make studioAlbum have the data type StudioAlbum?. That is, an optional studio album: the conversion might have worked, in which case you have a studio album you can work with, or it might have failed, in which case you have nil. This is most commonly used with if let to automatically unwrap the optional result, like this:
for album in allAlbums { print(album.getPerformance())
www.hackingwithswift.com
90
if let studioAlbum = album as? StudioAlbum { print(studioAlbum.studio) } else if let liveAlbum = album as? LiveAlbum { print(liveAlbum.location) } }
That will go through every album and print its name, because that's common to the Album class and all its subclasses. It then checks whether it can convert the album value into a StudioAlbum, and if it can it prints out the studio name. The same thing is done for the LiveAlbum in the array. Forced downcasting is when you're really sure an object of one type can be treated like a different type, but if you're wrong your program will just crash. Forced downcasting doesn't need to return an optional value, because you're saying the conversion is definitely going to work – if you're wrong, it means you wrote your code wrong. To demonstrate this in a non-crashy way, let's strip out the live album so that we just have studio albums in the array:
var taylorSwift = StudioAlbum(name: "Taylor Swift", studio: "The Castles Studios") var fearless = StudioAlbum(name: "Speak Now", studio: "Aimeeland Studio")
var allAlbums: [Album] = [taylorSwift, fearless]
for album in allAlbums { let studioAlbum = album as! StudioAlbum print(studioAlbum.studio) }
www.hackingwithswift.com
91
That's obviously a contrived example, because if that really were your code you would just change allAlbums so that it had the data type [StudioAlbum]. Still, it shows how forced downcasting works, and the example won't crash because it makes the correct assumptions. Swift lets you downcast as part of the array loop, which in this case would be more efficient. If you wanted to write that forced downcast at the array level, you would write this:
for album in allAlbums as! [StudioAlbum] { print(album.studio) }
That no longer needs to downcast every item inside the loop, because it happens when the loop begins. Again, you had better be correct that all items in the array are StudioAlbums, otherwise your code will crash. Swift also allows optional downcasting at the array level, although it's a bit more tricksy because you need to use the nil coalescing operator to ensure there's always a value for the loop. Here's an example:
for album in allAlbums as? [LiveAlbum] ?? [LiveAlbum]() { print(album.location) }
What that means is, "try to convert allAlbums to be an array of LiveAlbum objects, but if that fails just create an empty of live albums and use that instead" – i.e., do nothing. It's possible to use this, but I'm not sure you'd really want to!
www.hackingwithswift.com
92
Closures You've met integers, strings, doubles, floats, Booleans, arrays, dictionaries, structs and classes so far, but there's another type of data that is used extensively in Swift, and it's called a closure. These are complicated, but they are so powerful and expressive that they are used pervasively in Cocoa Touch, so you won't get very far without understanding them. A closure can be thought of as a variable that holds code. So, where an integer holds 0 or 500, a closure holds lines of Swift code. It's different to a function, though, because closures are a data type in their own right: you can pass a closure as a parameter or store it as a property. Closures also capture the environment where they are created, which means they take a copy of the values that are used inside them. You never need to design your own closures so don't be afraid if you find the following quite complicated. However, Cocoa Touch will often ask you to write closures to match its needs, so you at least need to know how they work. Let's take a Cocoa Touch example first:
let vw = UIView()
UIView.animateWithDuration(0.5, animations: { vw.alpha = 0 })
UIView is a data type in UIKit that represents the most basic kind of user interface container. Don't worry about what it does for now, all that matters is that it's the basic user interface component. UIView has a method called animateWithDuration() and it lets you change the way your interface looks using animation – you describe what's changing and over how many seconds, and Cocoa Touch does the rest. The animateWithDuration() method takes two parameters in that code: the number of seconds to animate over, and a closure containing the code to be executed as part of the animation. I've specified half a second as the first parameter, and for the second I've asked UIKit to adjust the view's alpha (that's opacity) to 0, which means "completely transparent." This method needs to use a closure because UIKit has to do all sorts of work to prepare for the animation to begin, so what happens is that UIKIt takes a copy of the code inside the
www.hackingwithswift.com
93
braces (that's our closure), stores it away, does all its prep work, then runs our code when it's ready. This wouldn't be possible if we just run our code directly. The above code also shows how closure's capture their environment: I declared the vw constant outside of the closure, then used it inside. Swift detects this, and makes that data available inside the closure too. Swift's system of automatically capturing a closure's environment is very helpful, but can occasionally trip you up: if object A stores a closure as a property, and that property also references object A, you have something called a strong reference cycle and you'll have unhappy users. This is a substantially more advanced topic than you need to know right now, so don't worry too much about it just yet.
Trailing closures As closures are used so frequently, Swift can apply a little syntactic sugar to make your code easier to read. The rule is this: if the last parameter to a method takes a closure, you can eliminate that parameter and instead provide it as a block of code. For example, we could convert the previous code to this:
let vw = UIView()
UIView.animateWithDuration(0.5) { vw.alpha = 0 }
It does make your code shorter and easier to read, so this syntax form – known as trailing closure syntax – is preferred.
www.hackingwithswift.com
94
Wrap up That's the end of our tour around the Swift programming language. I haven't tried to cover everything in the language, but that's OK because you have all the important stuff, all the sometimes-important stuff, and all the nice-to-know stuff – the interesting-but-rarely-used features you'll either come across in a later project or through extended experience with the language. If you find yourself a bit confused about how exactly some features are used, don't worry: as soon as you start coding apps with Swift things will start to make a lot more sense! At this point, you have two options: start on Project 1 of Hacking with Swift, where you make your initial iOS app and recap some of the basic features of Swift, or if you're really desperate to learn more about the language I have four articles that teach you the new features of Swift 2.0 by example.
www.hackingwithswift.com
95
Project 1 Storm Viewer Get started coding in Swift by making an image viewer app and learning key concepts.
www.hackingwithswift.com
96
Setting up In this project you'll produce an application that lets users scroll through a list of images, then select one to view. It's deliberately simple, because there are many other things you'll need to learn along the way, so strap yourself in – this is going to be long! Launch Xcode, and choose "Create a new project" from the welcome screen. Choose Master-Detail Application from the list and click Next. For Product Name enter Project1, then make sure you have Swift selected for language and Universal for devices.
Creating a new Master-Detail Application project in Xcode.
One of the fields you'll be asked for is "Organisation Identifier", which is a unique identifier usually made up of your personal web site domain name in reverse. For example, I would use com.hackingwithswift if I were making an app. You'll need to put something valid in there if you're deploying to devices, but otherwise you can just use com.example.
www.hackingwithswift.com
97
Setting your Organization Identifier in Xcode.
Important note: some of Xcode's project templates have checkboxes saying "Use Core Data", "Include Unit Tests" and "Include UI Tests". Please ensure these boxes are unchecked for this project and indeed all projects in this series. Now click Next again and you'll be asked where you want to save the project – your desktop is fine. Once that's done, you'll be presented with the example project that Xcode made for you. The first thing we need to do is make sure you have everything set up correctly, and that means running the project as-is. When you run a project, you get to choose what kind of device the iOS Simulator should pretend to be, or you can also select a physical device if you have one plugged in. These options are listed under the Product > Destination menu, and you should see iPad 2, iPad Air, iPhone 6, and so on. There's also a shortcut for this menu: at the top-left of Xcode's window is the play and stop button, but to the right of that it should say Project1 then a device name. You can click on that device name to select a different device.
www.hackingwithswift.com
98
For now, please choose iPhone 5s, and click the Play triangle button in the top-left corner. This will compile your code, which is the process of converting it to instructions that iPhones can understand, then launch the simulator and run the app. As you'll see when you interact with the app, this basic project adds dates to the table when you click the + button, you can delete them either by swiping or using the Edit button, and tapping on a date brings in a new screen showing the date in its center.
The basic Master-Detail Application project in Xcode.
You'll be starting and stopping projects a lot as you learn, so there are three basic tips you need to know: • You can run your project by pressing Cmd+R. This is equivalent to clicking the play button. • You can stop a running project by pressing Cmd+. • If you have made changes to a running project, just press Cmd+R again. Xcode will prompt you to stop the current run before starting another. Make sure you check the "Do not show this message again" box to avoid being bothered in the future.
www.hackingwithswift.com
99
This project is all about letting users select images to view, so you're going to need to import some pictures. Download the files for this project from GitHub, and look in the Project1 folder. You'll see another folder in there called Project1, and inside that a folder called Content. I want you to drag that Content folder straight into your Xcode project, just under where it says "Info.plist". A window will appear asking how you want to add the files: make sure "Copy items if needed" is checked, and "Create groups" is selected. Important: do not choose "Create folder references" otherwise your project will not work.
When you add items to Xcode, make sure you choose Create Folder References.
Click Finish and you'll see a yellow Content folder appear in Xcode. If you see a blue one, you didn't select "Create groups", and you'll have problems following this tutorial!
www.hackingwithswift.com
100
Deleting skeleton code Apple's example contains lots of code we don't need, so let's zap it: select the file MasterViewController.swift to open it for editing. Around line 17 you'll see the code starting with override func viewDidLoad() {, and there'll be some more lines of code until it reaches a } on a line all by itself on line 28. If you're not sure which } I mean, it's the one that is aligned directly beneath the first letter in override. No line numbers? If your Xcode isn't showing line numbers by default, I suggest you turn them on. Go to the Xcode menu and choose Preferences, then choose the Text Editing tab and make sure "Line numbers" is checked. This block of code is the viewDidLoad() method, which is code that gets called when the system has finished creating the screen and is giving you the chance to configure it. The method starts at the func viewDidLoad() { line and ends on the } not far below. These symbols, known as braces (or sometimes curly brackets) are used to mark chunks of code, and it's convention to indent lines inside braces so that it's easy to identify where code blocks start and end. But enough of the theory: almost everything inside this method is not needed, so delete its contents except for the line super.viewDidLoad(). Note: when I say "delete its contents" I mean leave the func viewDidLoad() { and } intact, but remove everything in between except for that one line. So, it should look like this:
override func viewDidLoad() { super.viewDidLoad() }
This method does nothing now, but that's OK: we'll be filling it in later. Next up, look for the insertNewObject() method, which starts with func insertNewObject() { and ends with a closing brace a few lines later. Delete the entire method – yes, even the func insertNewObject(sender: AnyObject) { and } parts. These methods start with func, which is short for "function", which for nearly all intents and purposes is identical to a method in Swift. There is one small exception, but you won't come
www.hackingwithswift.com
101
to it until project 24 so for now please just consider functions and methods identical. Finally, delete the very last method in the file. This one has a very peculiar name, and it's a bit of a hangover from Apple's previous language, Objective C. The method is quite long, and although we don't need it here, you do need to understand what it means. Here's the method definition:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
Contents About this Book
11
Introduction: Swift for Complete Beginners
15
How to install Xcode and create a playground Variables and constants Types of Data Operators String interpolation Arrays Dictionaries Conditional statements Loops Switch case Functions Optionals Optional chaining Enumerations Structs Classes Properties Static properties and methods Access control Polymorphism and type casting Closures Wrap up
Project 1: Storm Viewer
96
Setting up Deleting skeleton code Listing our images Introducing Interface Builder Sending new data Final tweaks Wrap up
Project 2: Guess the Flag
135
Setting up
www.hackingwithswift.com
2
Designing your layout Making the basic game work Guess which flag? From outlets to actions Wrap up
Project 3: Social Media
166
About technique projects Activity View Controllers Twitter and Facebook Wrap up
Project 4: Easy Browser
176
Setting up Creating a simple browser Choosing a website Monitoring page loads Refactoring for the win Wrap up
Project 5: Word Scramble
199
Setting up Reading from disk Pick a word, any word Prepare for submission Returning values Or else what? Wrap up
Project 6: Auto Layout
229
Setting up Advanced Auto Layout Auto Layout in code Sizes, metrics and priorities Wrap up
Project 7: Whitehouse Petitions
244
Setting up Creating the basic UI Parsing JSON Rendering a petition Finishing touches Wrap up
www.hackingwithswift.com
3
Wrap up
Project 8: 7 Swifty Words
267
Setting up Buttons… buttons everywhere. Loading a level It's play time! Property observers Wrap up
Project 9: Grand Central Dispatch
285
Setting up Why is locking the UI bad? GCD 101 Back to the main thread Wrap up
Project 10: Names to Faces
295
Setting up Designing UICollectionView cells Data sources and delegates Importing photos Custom classes Connecting up the people Wrap up
Project 11: Pachinko
317
Setting up Falling boxes Bouncing balls Spinning slots Collision detection Scores on the board Special effects Wrap up
Project 12: NSUserDefaults
347
Setting up Reading and writing basics Fixing Project 10 Wrap up
www.hackingwithswift.com
4
Project 13: Instafilter
358
Setting up Designing the interface Importing a picture Applying filters Saving to the photo library Wrap up
Project 14: Whack-a-Penguin
376
Setting up Getting up and running Penguin, show thyself Whack to win Wrap up
Project 15: Animation
398
Setting up Preparing for action Switch, case, animate Transform! Wrap up
Project 16: JavaScript Injection
412
Setting up Making a shell app Adding an extension What do you want to get? Establishing communication Hacking with JavaScript Fixing the keyboard Wrap up
Project 17: Swifty Ninja
434
Setting up Basics quick start Shaping up for action Enemy or bomb? Follow the sequence Slice to win Game over, man Wrap up
www.hackingwithswift.com
5
Project 18: iAd and Debugging
474
Setting up iAd in 10 minutes Debugging in Xcode Wrap up
Project 19: Capital Cities
486
Setting up Up and running with MapKit Annotations and accessory views Wrap up
Project 20: Fireworks Night
498
Setting up Ready... aim... fire! Swipe to select Making things go bang Wrap up
Project 21: Local Notifications
517
Setting up Scheduling notifications Acting on responses Wrap up
Project 22: Detect-a-Beacon
528
Setting up Requesting location Hunting the beacon Wrap up
Project 23: Space Race
540
Setting up Space: the final frontier Bring on the enemies! Making contact Wrap up
Project 24: Swift Extensions www.hackingwithswift.com
553 6
Setting up Adding to integers Cleaning up the mess Extensions for brevity Wrap up
Project 25: Selfie Share
567
Setting up Importing photos again Going peer to peer Invitation only Wrap up
Project 26: Marble Maze
584
Setting up Loading a level Tilt to move Contacting but not colliding Wrap up
Project 27: Core Graphics
605
Setting up Creating the sandbox Drawing into a context Ellipses and checkerboards Transforms and lines Images and text Wrap up
Project 28: Secret Swift
628
Setting up The basic text editor Writing somewhere safe Touch to activate Wrap up
Project 29: Exploding Monkeys
642
Setting up Building the environment Mixing UIKit and SpriteKit Unleash the bananas! Destructible terrain
www.hackingwithswift.com
7
Wrap up
Project 30: Instruments
673
Setting up What are we working with? What can Instruments tell us? Fixing the bugs Wrap up
Project 31: Multibrowser
687
Setting up UIStackView by example UIStackView in Swift 2 Removing views from a UIStackView iPad multitasking in iOS 9 Wrap up
Project 32: SwiftSearcher
708
Setting up Automatically resizing UITableViewCells with NSAttributedString and Dynamic Type How to use SFSafariViewController to browse a web page How to add Core Spotlight to index your app content Wrap up
Project 33: What's that Whistle?
733
Setting up Recording from the microphone: AVAudioRecorder Animating UIStackView subview layout Writing to iCloud with CloudKit: CKRecord and CKAsset A hands-on guide to the CloudKit dashboard Reading from iCloud with CloudKit: CKQueryOperation and NSPredicate Working with CloudKit records: CKReference, fetchRecordWithID, and saveRecord Delivering notifications with CloudKit push messages: CKSubscription and saveSubscription Wrap up
Project 34: Four in a Row
804
Setting up Creating the interface with UIStackView Preparing for basic play Adding in players: GKGameModelPlayer Detecting wins and draws in Four in a Row
www.hackingwithswift.com
8
How GameplayKit works: GKGameModel, GKGameModelPlayer and GKGameModelUpdate Implementing GKGameModel: gameModelUpdatesForPlayer() and applyGameModelUpdate() Creating a GameplayKit AI using GKMinmaxStrategist Wrap up
Project 35: Random Numbers
848
Setting up Generating random numbers in iOS 8 and earlier Generating random numbers with GameplayKit: GKRandomSource Choosing a random number source: GKARC4RandomSource and other GameplayKit options Shaping GameplayKit random numbers: GKRandomDistribution, GKShuffledDistribution and GKGaussianDistribution Shuffling an array with GameplayKit: arrayByShufflingObjectsInArray() Wrap up
Project 36: Crashy Plane
867
Setting up Creating a player: ResizeFill vs AspectFill Sky, background and ground: parallax scrolling with SpriteKit Creating collisions and making random numbers with GameplayKit Pixel-perfect physics in SpriteKit, plus explosions and more Background music with SKAudioNode, an intro, plus game over Wrap up
Project 37: Psychic Tester
901
Setting up Laying out the cards: addChildViewController() Animating a 3D flip effect using transitionWithView() Adding a CAGradientLayer with IBDesignable and IBInspectable Creating a particle system using CAEmitterLayer Wiggling cards and background music with AVAudioPlayer How to measure touch strength using 3D Touch Communicating between iOS and watchOS: WCSession Designing a simple watchOS app to receive data Wrap up
Project 38: GitHub Commits
941
Setting up Designing a Core Data model Adding Core Data to our project: NSPersistentStoreCoordinator
www.hackingwithswift.com
9
Creating an NSManagedObject subclass with Xcode Loading Core Data objects using NSFetchRequest and NSSortDescriptor How to make a Core Data attribute unique using constraints Examples of using NSPredicate to filter NSFetchRequest Adding Core Data entity relationships: lightweight vs heavyweight migration How to delete a Core Data object Optimizing Core Data Performance using NSFetchedResultsController Wrap up
Project 39: Unit testing with XCTest
998
Setting up Creating our first unit test using XCTest Loading our data and splitting up words: filter() Counting unique strings in an array measureBlock(): How to optimize our slow code and adjust the baseline Filtering using functions as parameters Updating the user interface with filtering User interface testing with XCTest Wrap up
Appendix: The Swift Knowledge Base
www.hackingwithswift.com
1043
10
About this book The Hacking with Swift tutorial series is designed to make it easy for beginners to get started coding for iPad and iPhone using the Swift programming language. My teaching method skips out a lot of theory. It skips out the smart techniques that transform 20 lines of easy-to-understand code into 1 line of near-magic. It ignores coding conventions by the dozen. And perhaps later on, once you've finished, you'll want to go back and learn all the theory I so blithely walked past. But let me tell you this: the problem with learning theory by itself is that your brain doesn't really have any interest in remembering stuff just for the sake of it. You see, here you'll be learning to code on a Need To Know basis. Nearly everything you learn from me will have a direct, practical application to something we're working on. That way, your brain can see exactly why a certain technique is helpful and you can start using it straight away. This book has been written on the back of my personal motto: "Programming is an art. Don't spend all your time sharpening your pencil when you should be drawing." We'll be doing some "sharpening" but a heck of a lot more "drawing" – if that doesn't suit your way of learning, you should exit now.
The three golden rules The series is crafted around a few basic tenets, and it's important you understand them before continuing: 1. Follow the series: The tutorials are designed to be used in order, starting at the beginning and working through to the end. The reason for this is that concepts are introduced sequentially on a need-to-know basis – you only learn about something when you really have to in order to make the project work. 2. Don't skip the games and techniques: The tutorials follow a sequence: app, game, technique, app, game, technique, etc. That is, you develop an app, then you develop a game, then we focus on a particular iOS component together to help make your apps better. The apps and games are, of course, standalone projects that you can go on to develop as
www.hackingwithswift.com
11
you wish, whereas the technique tutorials will often be used to improve or prepare you for other projects. 3. Get ready to hack: This is not designed to be the one-stop learning solution for all your Swift needs. It's called "Hacking with Swift" because the goal of each project is to reach the end with as little complication as possible – we're hacking, or playing around, with the language, not trying to give you Comp Sci 101. I can't re-iterate that last point enough. What I have found time and time again is that any tutorial, no matter how carefully written or what audience it's aimed at, will fail to fit the needs of many possible readers. And these people get angry, saying how the tutorial is wrong, how the tutorial is lame, how their tutorial would be much better if only they had the time to write it, and so on. Over the last 12 years of writing, I have learned to ignore minority whinging and move on, because what matters is that this tutorial is useful to you. You'd be surprised by how many people think the path to success is through reading books, attending classes or, well, doing pretty much anything except sitting down in front of a computer and typing. Not me. I believe the best way to learn something is try to it yourself and see how it goes. Sure, going to classes might re-enforce what you've learned, or it might teach you some time-saving techniques, but ultimately I've met too many people with computing degrees who stumble when asked to write simple programs. Don't believe me? Try doing a Google search for "fizz buzz test", and you'll be surprised too. So, dive in, make things, and please, please, please have fun – because if you're not enjoying yourself, Swift coding probably isn't for you. To download the files for any Hacking with Swift project, or if you'd like to learn more about the series, visit the series website and see the full range: hackingwithswift.com. If you spot any errors in this book, either typos or technical mistakes, please do let me know so I can correct them as soon as possible. The best way to get in touch is on Twitter @twostraws, but you can also email [email protected]
www.hackingwithswift.com
12
Xcode, Swift and iOS I'm not going to talk much, because I want to get straight into coding. However, there are some points you do need to know: • You should install the latest Xcode from the Mac App Store. It's free, and includes everything you need to make iOS apps in the iOS Simulator. Most of the projects in this series will be developed in the simulator, but a couple will require a device because the technology isn't available in the simulator – things like Touch ID and the accelerometer, for example. Projects that require a device also require you to have an active iOS developer account with Apple so that you can deploy your project to a device. • Swift is a relatively new language, and is evolving quickly. Every new release of Xcode seems to change something or other, and often that means code that used to work now no longer does. At the time of writing, Swift is mature enough that the changes are relatively minor, so hopefully you can make them yourself. If not, check to see if there's an update of the project files on hackingwithswift.com. • These projects are designed to work with iOS 9.0 or later, which is the version that runs on the majority of devices. You can downgrade them to 7.0 with relatively few changes if you desperately want to maximise your reach, but it's really not worth it at this point. Important note: if any bugs are found in the project files, or if Swift updates come out that force syntax changes, I'm going to be making changes to the projects on the website and updating this book as needed. Please make sure you read the release notes for each project to see what's changed, and follow me on Twitter @twostraws if you want to be notified of updates. I'm also happy to answer questions on Twitter if you encounter problems, so please feel free to get in touch! Swift, the Swift logo, Xcode, Instruments, Cocoa Touch, Touch ID, AirDrop, iBeacon, iPhone, iPad, Safari, App Store, Mac and OS X are trademarks of Apple Inc., registered in the U.S. and other countries. Hacking with Swift is copyright Paul Hudson. All rights reserved. No part of this book or corresponding materials (such as text, images, or source code) may be reproduced or distributed by any means without prior written permission of the copyright owner.
www.hackingwithswift.com
13
Dedication This book is dedicated to my daughter Charlotte, aka "Bonk", who has provided lots of hugs and lots of happiness at every point in its creation.
www.hackingwithswift.com
14
Introduction Swift for Complete Beginners If you want to learn the language all at once before you start making apps, this is for you.
www.hackingwithswift.com
15
How to install Xcode and create a playground Xcode is Apple's programming application for developers. It's free from the Mac App Store, and it's required to do iPhone and iPad development. So, your first action is to click here to install Xcode from the Mac App Store – it's quite a big download, so start downloading it now and carry on reading. While that's downloading, I can explain a couple of the absolute basics to you: • iOS is the name of the operating system that runs on all iPhones and iPads. It's responsible for all the basic operations of the phone, such as making phone calls, drawing on the screen, and running apps. • Swift is Apple's modern programming language that lets you write apps for iOS. It contains the functionality for building programs, but doesn't handle anything like user interfaces, audio or networking. • Swift 1.2 was the first major update to Swift, tweaking various language features and improving others. • Swift 2 is the second major update to Swift, and it's the version used across all of Hacking with Swift. • UIKit is Apple's user interface toolkit. It contains things like buttons, text boxes, navigation controls and more, and you drive it using Swift. • Cocoa Touch is the name given for Apple's vast collection of frameworks for iOS. It includes UIKit to do user interfaces, but also SpriteKit for making 2D games, SceneKit for making 3D games, MapKit for maps, Core Graphics for drawing, Core Animation for animating things, iAd for placing adverts, and much more. • NeXTSTEP is an operating system created by a company that Steve Jobs founded called NeXT. It was bought by Apple, at which point Jobs was placed back in control of the company, and put NeXTSTEP technology right into the core of Apple's development platform. • iOS Simulator is a tool that comes with Xcode that looks and works almost exactly like a real iPhone or iPad. It lets you test your app very quickly without having to use a real device. • Playgrounds are miniature Swift testing environments that let you type code and see the results immediately. You don't build real apps with them, but they are great for learning. We'll be using playgrounds in this introduction. • Crashes are when your code goes disastrously wrong and your app cannot recover. If a user is running your app it will just disappear and they'll be back on the home screen. If you're running in Xcode, you'll see a crash report.
www.hackingwithswift.com
16
• Taylor Swift has nothing to do with the Swift programming language. This is a shame, as you might imagine, but I'll try to make up for this shortfall by using her songs in this tutorial. Deal with it. That's it for the basics – if Xcode still hasn't finished downloading then why not watch some Taylor Swift videos while you wait? The examples in this tutorial will certainly make a lot more sense…
www.hackingwithswift.com
17
Variables and constants Every useful program needs to store data at some point, and in Swift there are two ways to do it: variables and constants. A variable is a data store that can have its value changed whenever you want, and a constant is a data store that you set once and can never change. So, variables have values that can vary, and constants have values that are constant – easy, right? Having both these options might seem pointless, after all you could just create a variable then never change it – why does it need to be made a constant? Well, it turns out that many programmers are – shock! – less than perfect at programming, and we make mistakes. One of the advantages of separating constants and variables is that Xcode will tell us if we've made a mistake. If we say, "make this date a constant, because I know it will never change" then 10 lines later try to change it, Xcode will refuse to build our app. Constants are also important because they let Xcode make decisions about the way it builds your app. If it knows a value will never change, it is able to apply optimizations to make your code run faster. In Swift, you make a variable using the var keyword, like this:
var name = "Tim McGraw"
Let's put that into a playground so you can start getting feedback. Delete everything in there apart from the import UIKit line (that's the bit that pulls in Apple's core iOS framework and it's need later on), and add that variable. You should see the picture below.
www.hackingwithswift.com
18
In Xcode playgrounds, you type your code on the left and see results on the right a second later.
Because this is a variable, you can change it whenever you want, but you shouldn't use the var keyword each time – that's only used when you're declaring new variables. Try writing this:
var name = "Tim McGraw" name = "Romeo"
So, the first line creates the name variable and gives it an initial value, then the second line updates the name variable so that its value is now "Romeo". You'll see both values printed in the results area of the playground. Now, what if we had made that a constant rather than a variable? Well, constants use the let keyword rather than var, so you can change your first line of code to say let name rather than var name like this:
import UIKit let name = "Tim McGraw" name = "Romeo"
But now there's a problem: Xcode is showing a red warning symbol next to line three, and it should have drawn a squiggly underline underneath name. If you click the red warning
www.hackingwithswift.com
19
should have drawn a squiggly underline underneath name. If you click the red warning symbol, Xcode will tell you the problem: "Cannot assign to 'let' value 'name'" – which is Xcode-speak for "you're trying to change a constant and you can't do that."
If you try to change a constant in Swift, Xcode will refuse to build your app.
So, constants are a great way to make a promise to Swift and to yourself that a value won't change, because if you do try to change it Xcode will refuse to run. Swift developers have a strong preference to use constants wherever possible because it makes your code easier to understand. In fact, in the very latest versions of Swift, Xcode will actually tell you if you make something a variable then never change it! Important note: variable and constant names must be unique in your code. You'll get an error if you try to use the same variable name twice, like this:
var name = "Tim McGraw" var name = "Romeo"
If the playground finds an error in your code, it will either flag up a warning in a red box, or will just refuse to run. You'll know if the latter has happened because the text in the results pane has gone gray rather than its usual black.
www.hackingwithswift.com
20
Types of Data There are lots of kinds of data, and Swift handles them all individually. You already saw one of the most important types when you assigned some text to a variable, but in Swift these are called a String – literally a string of characters. Strings can be long (e.g. a million letters or more), short (e.g. 10 letters) or even empty (no letters), it doesn't matter: they are all strings in Swift's eyes, and all work the same. Swift knows that name should hold a string because you assign a string to it when you create it: "Tim McGraw". If you were to rewrite your code to this it would stop working:
var name name = "Tim McGraw"
This time Xcode will give you an error message that won't make much sense just yet: "Type annotation missing in pattern". What it means is, "I can't figure out what data type name is because you aren't giving me enough information." At this point you have two options: either create your variable and give it an initial value on one line of code, or use what's called a type annotation, which is where you tell Swift what data type the variable will hold later on, even though you aren't giving it a value right now. You've already seen how the first option looks, so let's look at the second: type annotations. We know that name is going to be a string, so we can tell Swift that by writing a colon then String, like this:
var name: String name = "Tim McGraw"
You'll have no errors now, because Swift knows what type of data name will hold in the future. Note: some people like to put a space before and after the colon, making var name : String, but they are wrong and you should try to avoid mentioning their wrongness in polite
www.hackingwithswift.com
21
company. The lesson here is that Swift always wants to know what type of data every variable or constant will hold. Always. You can't escape it, and that's a good thing because it provides something called type safety – if you say "this will hold a string" then later try and put a rabbit in there, Swift will refuse. We can try this out now by introducing another important data type, called Int, which is short for "integer." Integers are round numbers like 3, 30, 300, or -16777216. For example:
var name: String name = "Tim McGraw"
var age: Int age = 25
That declares one variable to be a string and one to be an integer. Note how both String and Int have capital letters at the start, whereas name and age do not – this is the standard coding convention in Swift. A coding convention is something that doesn't matter to Swift (you can write your names how you like!) but does matter to other developers. In this case, data types start with a capital letter, whereas variables and constants do not. Now that we have variables of two different types, you can see type safety in action. Try writing this:
name = 25 age = "Time McGraw"
In that code, you're trying to put an integer into a string variable, and a string into an integer variable – and, thankfully, Xcode will throw up errors. You might think this is pedantic, but it's actually quite helpful: you make a promise that a variable will hold one particular type of data, and Xcode will enforce that throughout your work. Before you go on, please delete those two lines of code causing the error, otherwise
www.hackingwithswift.com
22
Before you go on, please delete those two lines of code causing the error, otherwise nothing in your playground will work going forward!
Float and Double Let's look at two more data types, called Float and Double. This is Swift's way of storing numbers with a fractional component, such as 3.1, 3.141, 3.1415926, and -16777216.5. There are two data types for this because you get to choose how much accuracy you want, but most of the time it doesn't matter so the official Apple recommendation is always to use Double because it has the highest accuracy. Try putting this into your playground:
var latitude: Double latitude = 36.166667
var longitude: Float longitude = -86.783333
You can see both numbers appear on the right, but look carefully because there's a tiny discrepancy. We said that longitude should be equal to -86.783333, but in the results pane you'll see -86.78333 – it's missing one last 3 on the end. Now, you might well say, "what does 0.000003 matter amongst friends?" but this is ably demonstrating what I was saying about accuracy. Because these playgrounds update as you type, we can try things out so you can see exactly how Float and Double differ. Try changing the code to be this:
var longitude: Float longitude = -86.783333 longitude = -186.783333 longitude = -1286.783333 longitude = -12386.783333 longitude = -123486.783333
www.hackingwithswift.com
23
longitude = -123486.783333 longitude = -1234586.783333
That's adding increasing numbers before the decimal point, while keeping the same amount of numbers after. But if you look in the results pane you'll notice that as you add more numbers before the point, Swift is removing numbers after. This is because it has limited space in which to store your number, so it's storing the most important part first – being off by 1,000,000 is a big thing, whereas being off by 0.000003 is less so.
In Swift a Float holds much less data than a Double, so you should use Double where possible.
Now try changing the Float to be a Double and you'll see Swift prints the correct number out every time:
var longitude: Double
This is because, again, Double has twice the accuracy of Float so it doesn't need to cut your
www.hackingwithswift.com
24
number to fit. Doubles still have limits, though – if you were to try a massive number like 123456789.123456789 you would see it gets cut down to 123456789.1234568.
Boolean Swift has a built-in data type that can store whether a value is true or false, called a Bool, short for Boolean. Bools don't have space for "maybe" or "perhaps", only absolutes: true or false. For example:
var stayOutTooLate: Bool stayOutTooLate = true
var nothingInBrain: Bool nothingInBrain = true
var missABeat: Bool missABeat = false
Using type annotations wisely As you've learned, there are two ways to tell Swift what type of data a variable holds: assign a value when you create the variable, or use a type annotation. If you have a choice, the first is always preferable because it's clearer. For example:
var name = "Tim McGraw"
www.hackingwithswift.com
25
…is preferred to:
var name: String name = "Tim McGraw"
This applies to all data types. For example:
var age = 25 var longitude = -86.783333 var nothingInBrain = true
This technique is called type inference, because Swift can infer what data type should be used for a variable by looking at the type of data you want to put in there. When it comes to numbers like -86.783333, Swift will always infer a Double rather than a Float. For the sake of completeness, I should add that it's possible to specify a data type and provide a value at the same time, like this:
var name: String = "Tim McGraw"
www.hackingwithswift.com
26
Operators Operators are those little symbols you learned in your very first math classes: + to add, - to subtract, * to multiply, / to divide, = to assign value, and so on. They all exist in Swift, along with a few extras. Let's try a few basics – please type this into your playground:
var a = 10 a = a + 1 a = a - 1 a = a * a
In the results pane, you'll see 10, 11, 10 and 100 respectively. Now try this:
var b = 10 b += 10 b -= 10
+= is an operator that means "add then assign to." In our case it means "take the current value of b, add 10 to it, then put the result back into b." As you might imagine, -= does the same but subtracts rather than adds. So, that code will show 10, 20, 10 in the results pane. Some of these operators apply to other data types. As you might imagine, you can add two doubles together like this:
var a = 1.1 var b = 2.2 var c = a + b
www.hackingwithswift.com
27
When it comes to strings, + will join them together. For example:
var name1 = "Tim McGraw" var name2 = "Romeo" var both = name1 + " and " + name2
That will write "Tim McGraw and Romeo" into the results pane.
Comparison operators Swift has a set of operators that perform comparisons on values. For example:
var a = 1.1 var b = 2.2 var c = a + b
c > 3 c >= 3 c > 4 c < 4
That shows off greater than (>), greater than or equal (>=), and less than ( String? { return "Hate" }
Note the extra question mark: that means "optional string." Now, in our case we're still returning "Hate" no matter what, but let's go ahead and modify that function further: if the weather is sunny, the haters have turned over a new leaf and have given up their life of
www.hackingwithswift.com
54
hating, so want to return no value. In Swift, this "no value" has a special name: nil. Change the function to this:
func getHaterStatus(weather: String) -> String? { if weather == "sunny" { return nil } else { return "Hate" } }
That accepts one string parameter (the weather) and returns one string (hating status), but that return value might be there or it might not – it's nil. In this case, it means we might get a string, or we might get nil. Now for the important stuff: Swift wants your code to be really safe, and trying to use a nil value is a bad idea – it might crash your code, it might screw up your app logic, or it might make your user interface show the wrong thing. As a result, when you declare a value as being optional, Swift will make sure you handle it safely. Let's try this now: add these lines of code to your playground:
var status: String status = getHaterStatus("rainy")
The first line creates a string variable, and the second assigns to it the value from getHaterStatus() – and today the weather is rainy, so those haters are hating for sure. That code will not run, because we said that status is of type String, which requires a value, but getHaterStatus() might not provide one because it returns an optional string. Swift simply will not let you make this mistake, which is extremely helpful because it effectively
www.hackingwithswift.com
55
stops dead a whole class of common bugs. To fix the problem, we need to make the status variable a String?, or just remove the type annotation entirely and let Swift use type inference. The first option looks like this:
var status: String? status = getHaterStatus("rainy")
And the second like this:
var status = getHaterStatus("rainy")
Regardless of which you choose, that value might be there or might not, and by default Swift won't let you use it dangerously. As an example, imagine a function like this:
func takeHaterAction(status: String) { if status == "Hate" { print("Hating") } }
That takes a string and prints a message depending on its contents. This function takes a String value, and not a String? value – you can't pass in an optional here, it wants a real string, which means we can't call it using the status variable. Swift has two solutions. Both are used, but one is definitely preferred over the other. The first solution is called optional unwrapping, and it's done inside a conditional statement using special syntax. It does two things at the same time: checks whether an optional has a value, and if so unwraps it into a non-optional type then runs a code block. The syntax looks like this:
www.hackingwithswift.com
56
The syntax looks like this:
if let unwrappedStatus = status { // unwrappedStatus contains a non-optional value! } else { // in case you you want an else block, here you go… }
These if let statements check and unwrap in one succinct line of code, which makes them very common. Using this method, we can safely unwrap the return value of getHaterStatus() and be sure that we only call takeHaterAction() with a valid, non-optional string. Here's the complete code:
func getHaterStatus(weather: String) -> String? { if weather == "sunny" { return nil } else { return "Hate" } }
func takeHaterAction(status: String) { if status == "Hate" { print("Hating") } }
if let status = getHaterStatus("rainy") { takeHaterAction(status) }
www.hackingwithswift.com
57
If you understand this concept, you're welcome to skip down to the title that says "Force unwrapping optionals". If you're still not quite sure about optionals, carry on reading.OK, if you're still here it means the explanation above either made no sense, or you sort of understood but could probably use some clarification. Optionals are used extensively in Swift, so you really do need to understand them. I'm going to try explaining again, in a different way, and hopefully that will help! Here's a new function:
func yearAlbumReleased(name: String) -> Int { if name == "Taylor Swift" { return 2006 } if name == "Fearless" { return 2008 } if name == "Speak Now" { return 2010 } if name == "Red" { return 2012 } if name == "1989" { return 2014 }
return 0 }
That takes the name of a Taylor Swift album, and returns the year it was released. But if we call it with the album name "Lantern" because we mixed up Taylor Swift with Hudson Mohawke (an easy mistake to make, right?) then it returns 0 because it's not one of Taylor's albums. But does 0 make sense here? Sure, if the album was released back in 0 AD when Caesar Augustus was emperor of Rome, 0 might make sense, but here it's just confusing – people need to know that 0 means "not recognized." A much better idea is to re-write that function so that it either returns an integer (when a year was found) or nil (when nothing was found), which is easy thanks to optionals. Here's the new function:
func yearAlbumReleased(name: String) -> Int? { if name == "Taylor Swift" { return 2006 } if name == "Fearless" { return 2008 }
www.hackingwithswift.com
58
if name == "Fearless" { return 2008 } if name == "Speak Now" { return 2010 } if name == "Red" { return 2012 } if name == "1989" { return 2014 }
return nil }
Now that it returns nil, we need to unwrap the result using if let because we need to check whether a value exists or not. If you understand this concept, you're welcome to skip down to the title that says "Implict and force unwrapping optionals". If you're still not quite sure about optionals, carry on reading.OK, if you're still here it means you're really struggling with optionals, so I'm going to have one last go at explaining them. Here's an array of names:
var items = ["James", "John", "Sally"]
If we wanted to write a function that looked in that array and told us the index of a particular name, we might write something like this:
func positionOfString(items: [String], str: String) -> Int { for i in 0 ..< items.count { if items[i] == str { return i } }
return 0
www.hackingwithswift.com
59
return 0 }
That loops through all the items in the array, returning its position if it finds a match, otherwise returning 0. Now try running these three lines of code:
let jamesPosition = positionOfString(items, str: "James") let johnPosition = positionOfString(items, str: "John") let sallyPosition = positionOfString(items, str: "Sally") let bobPosition = positionOfString(items, str: "Bob")
That will output 0, 1, 2, 0 – the positions of James and Bob are the same, even though one exists and one doesn't. This is because I used 0 to mean "not found." The easy fix might be to make -1 not found, but whether it's 0 or -1 you still have a problem because you have to remember that specific number means "not found." The solution is optionals: return an integer if you found the match, or nil otherwise. In fact, this is exactly the approach the built-in "find in array" functions work: someArray.indexOf(someValue). When you work with these "might be there, might not be" values, Swift forces you to unwrap them before using them, thus acknowledging that there might not be a value. That's what if let syntax does: if the optional has a value then unwrap it and use it, otherwise don't use it at all. If you're still not sure how optionals work, then the best thing to do is ask me on Twitter and I'll try to help: you can find me @twostraws.
Force unwrapping optionals
www.hackingwithswift.com
60
Swift lets you override its safety by using the exclamation mark character: !. If you know that an optional definitely has a value, you can force unwrap it by placing this exclamation mark after it. Please be careful, though: if you try this on a variable that does not have a value, your code will crash. To put together a working example, here's some foundation code:
func yearAlbumReleased(name: String) -> Int? { if name == "Taylor Swift" { return 2006 } if name == "Fearless" { return 2008 } if name == "Speak Now" { return 2010 } if name == "Red" { return 2012 } if name == "1989" { return 2014 }
return nil }
var year = yearAlbumReleased("Red")
if year == nil { print("There was an error") } else { print("It was released in \(year)") }
That gets the year an album was released. If the album couldn't be found, year will be set to nil, and an error message will be printed. Otherwise, the year will be printed. Or will it? Well, yearAlbumReleased() returns an optional string, and this code doesn't use if let to unwrap that optional. As a result, it will print out the following: "It was released in Optional(2012)" – probably not what we wanted!
www.hackingwithswift.com
61
At this point in the code, we have already checked that we have a valid value, so it's a bit pointless to have another if let in there to safely unwrap the optional. So, Swift provides a solution – change the second print() call to this:
print("It was released in \(year!)")
Note the exclamation mark: it means "I'm certain this contains a value, so force unwrap it now."
Implicitly unwrapped optionals You can also use this exclamation mark syntax to create implicitly unwrapped optionals, which is where some people really start to get confused. So, please read this carefully! • A regular variable must contain a value. Example: String must contain a string, even if that is string empty, i.e. "". • An optional variable might contain a value, or might not. It must be unwrapped before it is used. Example: String? might contain a string, or it might contain nil. The only way to find out is to unwrap it. • An implicitly unwrapped optional might contain a value, or might not. But it does not need to be unwrapped before it is used. Swift won't check for you, so you need to be extra careful. Example: String! might contain a string, or it might contain nil – and it's down to you to use it appropriately. There are two main times you're going to meet implicitly unwrapped optionals. The first is when you're working with Apple's APIs: these frequently return implicitly unwrapped optionals because their code pre-dates Swift and that was how things were done in Ye Olde Ages Of Programming. The second is when you're working with user interface elements in UIKit. These need to be declared up front, but you can't use them until they have been created by iOS – and it likes to create user interface elements at the last possible moment to avoid any unnecessary work. Having to continually unwrap values you definitely know will be there is annoying, so these are made implicitly unwrapped.
www.hackingwithswift.com
62
Don't worry if you find implicitly unwrapped optionals a bit hard to grasp - it will become clear as you work with the language.
www.hackingwithswift.com
63
Optional chaining Working with optionals can feel a bit clumsy sometimes, and all the unwrapping and checking can become so onerous that you might be tempted to throw some exclamation marks to force unwrap stuff so you can get on with work. Be careful, though: if you force unwrap an optional that doesn't have a value, your code will crash. Swift has two techniques to help make your code less complicated. The first is called optional chaining, which lets you run code only if your optional has a value. Put the below code into your playground to get us started:
func albumReleasedYear(year: Int) -> String? { switch year { case 2006: return "Taylor Swift" case 2008: return "Fearless" case 2010: return "Speak Now" case 2012: return "Red" case 2014: return "1989" default: return nil } }
let album = albumReleasedYear(2006) print("The album is \(album)")
That will output "The album is Optional("Taylor Swift")" into the results pane. If we wanted to convert the return value of albumReleasedYear() to be uppercase letters (that is, "TAYLOR SWIFT" rather than "Taylor Swift") we could use the uppercaseString value of that string. For example:
let str = "Hello world" print(str.uppercaseString)
www.hackingwithswift.com
64
print(str.uppercaseString)
The problem is, albumReleasedYear() returns an optional string: it might return a string or it might return nothing at all. So, what we really mean is, "if we got a string back make it uppercase, otherwise do nothing." And that's where optional chaining comes in, because it provides exactly that behavior. Try changing the last two lines of code to this:
let album = albumReleasedYear(2006)?.uppercaseString print("The album is \(album)")
Note that there's a question mark in there, which is the optional chaining: everything after the question mark will only be run if everything before the question mark has a value. This doesn't affect the underlying data type of album, because that line of code will now either return nil or will return the uppercase album name – it's still an optional string. Your optional chains can be as long as you need, for example:
let album = albumReleasedYear(2006)?.someOptionalValue?.someOtherOptionalValue?.w hatever
Swift will check them from left to right until it finds nil, at which point it stops.
The nil coalescing operator This simple Swift feature makes your code much simpler and safer, and yet has such a grandiose name that many people are scared of it. This is a shame, because the nil coalescing operator will make your life easier if you take the time to figure it out!
www.hackingwithswift.com
65
What it is does is let you say "use value A if you can, but if value A is nil then use value B." That's it. It's particularly helpful with optionals, because it effectively stops them from being optional because you provide a non-optional value B. So, if A is optional and has a value, it gets used (we have a value.) If A is present and has no value, B gets used (so we still have a value). Either way, we definitely have a value. To give you a real context, try using this code in your playground:
let album = albumReleasedYear(2006) ?? "unknown" print("The album is \(album)")
That double question mark is the nil coalescing operator, and in this situation it means "if albumReleasedYear() returned a value then put it into the album variable, but if albumReleasedYear() returned nil then use 'unknown' instead." If you look in the results pane now, you'll see "The album is Taylor Swift" printed in there – no more optionals. This is because Swift can now be sure it will get a real value back, either because the function returned one or because you're providing "unknown". This in turn means you don't need to unwrap anything or risk crashes – you're guaranteed to have real data to work with, which makes your code safer and easier to work with.
www.hackingwithswift.com
66
Enumerations Enumerations – usually just called "enum" and pronounced "ee-num" - are a way for you to define your own kind of value in Swift. In some programming languages they are simple little things, but Swift adds a huge amount of power to them if you want to go beyond the basics. Let's start with a simple example from earlier:
func getHaterStatus(weather: String) -> String? { if weather == "sunny" { return nil } else { return "Hate" } }
That function accepts a string that defines the current weather. The problem is, a string is a poor choice for that kind of data – is it "rain", "rainy" or "raining"? Or perhaps "showering", "drizzly" or "stormy"? Worse, what if one person writes "Rain" with an uppercase R and someone else writes "Ran" because they weren't looking at what they typed? Enums solve this problem by letting you define a new data type, then define the possible values it can hold. For example, we might say there are five kinds of weather: sun, cloud, rain, wind and snow. If we make this an enum, it means Swift will accept only those five values – anything else will trigger an error. And behind the scenes enums are usually just simple numbers, which are a lot faster than strings for computers to work with. Let's put that into code:
enum WeatherType { case Sun, Cloud, Rain, Wind, Snow }
func getHaterStatus(weather: WeatherType) -> String? {
www.hackingwithswift.com
67
func getHaterStatus(weather: WeatherType) -> String? { if weather == WeatherType.Sun { return nil } else { return "Hate" } }
getHaterStatus(WeatherType.Cloud)
Take a look at the first three lines: line 1 gives our type a name, WeatherType. This is what you'll use in place of String or Int in your code. Line 2 defines the five possible cases our enum can be, as I already outlined. Convention has these start with a capital letter, so Sun, Cloud, etc. And line 3 is just a closing brace, ending the enum. Now take a look at how it's used: I modified the getHaterStatus() so that it takes a WeatherType value. The conditional statement is also rewritten to compare against WeatherType.Sun, which is our value. Remember, this check is just a number behind the scenes, which is lightning fast. Now, go back and read that code again, because I'm about to rewrite it with two changes that are important. All set?
enum WeatherType { case Sun case Cloud case Rain case Wind case Snow }
func getHaterStatus(weather: WeatherType) -> String? { if weather == .Sun {
www.hackingwithswift.com
68
if weather == .Sun { return nil } else { return "Hate" } }
getHaterStatus(.Cloud)
I made two differences there. First, each of the weather types are now on their own line. This might seem like a small change, and indeed in this example it is, but it becomes important soon. The second change was that I wrote if weather == .Sun – I didn't need to spell out that I meant WeatherType.Sun because Swift knows I am comparing against a WeatherType variable, so it's using type inference. Note that at this time Xcode is unable to use code completion to suggest enums if you use this short form. If you type them in full, e.g. WeatherType.Sun, you will get code completion. Enums are particularly useful inside switch/case blocks, particularly because Swift knows all the values your enum can have so it can ensure you cover them all. For example, we might try to rewrite the getHaterStatus() method to this:
func getHaterStatus(weather: WeatherType) -> String? { switch weather { case .Sun: return nil case .Cloud, .Wind: return "dislike" case .Rain: return "hate" } }
www.hackingwithswift.com
69
Yes, I realise "haters gonna dislike" is hardly a great lyric, but its academic anyway because this code won't build: it doesn't handle the .Snow case, and Swift wants all cases to be covered. You either have to add a case for it or add a default case.
Enums with additional values One of the most powerful features of Swift is that enumerations can have values attached to them that you define. To extend our increasingly dubious example a bit further, I'm going to add a value to the .Wind case so that we can say how fast the wind is. Modify your code to this:
enum WeatherType { case Sun case Cloud case Rain case Wind(speed: Int) case Snow }
As you can see, the other cases don't need a speed value – I put that just into wind. Now for the real magic: Swift lets us add extra conditions to the swift/case block so that a case will match only if those conditions are true. This uses the let keyword to access the value inside a case, then the where keyword for pattern matching. Here's the new function:
func getHaterStatus(weather: WeatherType) -> String? { switch weather { case .Sun:
www.hackingwithswift.com
70
case .Sun: return nil case .Wind(let speed) where speed < 10: return "meh" case .Cloud, .Wind: return "dislike" case .Rain, .Snow: return "hate" } }
getHaterStatus(WeatherType.Wind(speed: 5))
You can see .Wind appears in there twice, but the first time is true only if the wind is slower than 10 kilometers per hour. If the wind is 10 or above, that won't match. The key is that you use let to get hold of the value inside the enum (i.e. to declare a constant name you can reference) then use a where condition to check. Swift evaluates switch/case from top to bottom, and stops as soon as it finds match. This means that if case .Cloud, .Wind: appears before case .Wind(let speed) where speed < 10: then it will be executed instead – and the output changes. So, think carefully about how you order cases!
www.hackingwithswift.com
71
Structs Structs are complex data types, meaning that they are made up of multiple values. You then create an instance of the struct and fill in its values, then you can pass it around as a single value in your code. For example, we could define a Person struct type that contains two properties: clothes and shoes:
struct Person { var clothes: String var shoes: String }
When you define a struct, Swift makes them very easy to create because it automatically generates what's called a memberwise initializer. In plain speak, it means you create the struct by passing in initial values for its two properties, like this:
let taylor = Person(clothes: "T-shirts", shoes: "sneakers") let other = Person(clothes: "short skirts", shoes: "high heels")
Once you have created an instance of a struct, you can read its properties just by writing the name of the struct, a period, then the property you want to read:
print(taylor.clothes) print(other.shoes)
If you assign one struct to another, Swift copies it behind the scenes so that it is a complete, standalone duplicate of the original. Well, that's not strictly true: Swift uses a technique called "copy on write" which means it only actually copies your data if you try to change it. To help you see how struct copies work, put this into your playground:
www.hackingwithswift.com
72
struct Person { var clothes: String var shoes: String }
let taylor = Person(clothes: "T-shirts", shoes: "sneakers") let other = Person(clothes: "short skirts", shoes: "high heels")
var taylorCopy = taylor taylorCopy.shoes = "flip flops"
taylor taylorCopy
That creates two Person structs, then creates a third one called taylorCopy as a copy of taylor. What happens next is the interesting part: the code changes taylorCopy, and prints both it and taylor. If you look in your results pane (you might need to resize it to fit) you'll see that the copy has a different value to the original: changing one did not change the other.
www.hackingwithswift.com
73
Classes Swift has another way of build complex data types called classes. They look similar to structs, but have a number of important differences, including: • You don't get an automatic memberwise initializer for your classes; you need to write your own. • You can define a class as being based off another class, adding any new things you want. • If you copy an object, both copies point at the same data by default. All three of those are massive differences, so I'm going to cover them in more depth before continuing.
Initializing an object If we were to convert our Person struct into a Person class, Swift wouldn't let us write this:
class Person { var clothes: String var shoes: String }
This is because we're declaring the two properties to be String, which if you remember means they absolutely must have a value. This was fine in a struct because Swift automatically produces a memberwise initializer for us that forced us to provide values for the two properties, but this doesn't happen with classes so Swift can't be sure they will be given values. There are three solutions: make the two values optional strings, give them default values, or write our own initializer. The first option is clumsy because it introduces optionals all over our code where they don't need to be. The second option works, but it's a bit wasteful unless those default values will actually be used. That leaves the third option, and really it's the right one: write our own initializer.
www.hackingwithswift.com
74
To do this, create a function inside the class called init() that takes the two parameters we care about:
class Person { var clothes: String var shoes: String
init(clothes: String, shoes: String) { self.clothes = clothes self.shoes = shoes } }
There are two things that might jump out at you in that code. First, you don't write func before your init() function, because it's special. Second, because the parameter names being passed in are the same as the names of the properties we want to assign, you use self. to make your meaning clear – "the clothes property of this object should be set to the clothes parameter that was passed in." You can give them unique names if you want – it's down to you. There are two more things you ought to know but can't see in that code. First, when you write a function inside a class, it's called a method instead. In Swift you write func whether it's a function or a method, but the distinction is preserved when you talk about them. Second, Swift requires that all non-optional properties have a value by the end of the initializer, or by the time the initializer calls any other method – whichever comes first.
Class inheritance The second difference between classes and structs are that classes can build on each other to produce greater things, known as class inheritance. This is a technique used extensively in
www.hackingwithswift.com
75
to produce greater things, known as class inheritance. This is a technique used extensively in Cocoa Touch, even in the most basic programs, so it's something you should get to grips with. Let's start with something simple: a Singer class that has properties, which is their name and age. As for methods, there will a simple initializer to handle setting the properties, plus a sing() method that outputs some words:
class Singer { var name: String var age: Int
init(name: String, age: Int) { self.name = name self.age = age }
func sing() { print("La la la la") } }
We can now create an instance of that object by calling that initializer, then read out its properties and call its method:
var taylor = Singer(name: "Taylor", age: 25) taylor.name taylor.age taylor.sing()
That's our basic class, but we're going to build on it: I want to define a CountrySinger class
www.hackingwithswift.com
76
that has everything the Singer class does, but when I call sing() on it I want to print "Trucks, girls, and liquor" instead. You could of course just copy and paste the original Singer into a new class called CountrySinger but that's a lazy way to program and it will come back to haunt if you make later changes to Singer and forget to copy them across. Instead, Swift has a smarter solution: we can define CountrySinger as being based off Singer and it will get all its properties and methods for us to build on:
class CountrySinger: Singer {
}
That colon is what does the magic: it means "CountrySinger extends Singer." Now, that new CountrySinger class (called a subclass) doesn't add anything to Singer (called the parent class, or superclass) yet. We want it to have its own sing() method, but in Swift you need to learn a new keyword: override. This means "I know this method was implemented by my parent class, but I want to change it for this subclass." Having the override keyword is helpful, because it makes your intent clear. It also allows Swift to check your code: if you don't use override Swift won't let you change a method you got from your superclass, or if you use override and there wasn't anything to override, Swift will point out your error. So, we need to use override func, like this:
class CountrySinger : Singer { override func sing() { print("Trucks, girls, and liquor") } }
www.hackingwithswift.com
77
Now modify the way the taylor object is created:
var taylor = CountrySinger(name: "Taylor", age: 25) taylor.sing()
If you change CountrySinger to just Singer you should be able to see the different messages appearing in the results pane. Now, to make things more complicated, we're going to define a new class called HeavyMetalSinger. But this time we're going to store a new property called noiseLevel defining how loud this particular heavy metal singer likes to scream down their microphone. This causes a problem, and it's one that needs to be solved in a very particular way: • Swift wants all non-optional properties to have a value. • Our Singer class doesn't have a noiseLevel property. • So, we need to create a custom initializer for HeavyMetalSinger that accepts a noise level. • That new initializer also needs to know the name and age of the heavy metal singer, so it can pass it onto the superclass Singer. • Passing on data to the superclass is done through a method call, and you can't make method calls in initializers until you have given all your properties values. • So, we need to set our own property first (noiseLevel) then pass on the other parameters for the superclass to use. That might sound awfully complicated, but in code it's straightforward. Here's the HeavyMetalSinger class, complete with its own sing() method:
class HeavyMetalSinger : Singer { var noiseLevel: Int
init(name: String, age: Int, noiseLevel: Int) { self.noiseLevel = noiseLevel super.init(name: name, age: age) }
www.hackingwithswift.com
78
}
override func sing() { print("Grrrrr rargh rargh rarrrrgh!") } }
Notice how its initializer takes three parameters, then calls super.init() to pass name and age on to the Singer superclass - but only after its own property has been set. You'll see super used a lot when working with objects, and it just means "call a method on the class I inherited from. It's usually used to mean "let my parent class do everything it needs to do first, then I'll do my extra bits." Class inheritance is a big topic so don't fret if it's not clear just yet. However, there is one more thing you need to know: class inheritance often spans many levels. For example, A could inherit from B, and B could inherit from C, and C could inherit from D, and so on. This lets you build functionality and re-use up over a number of classes, helping to keep your code modular and easy to understand.
Values vs References When you copy a struct, the whole thing is duplicated, including all its values. This means that changing one copy of a struct doesn't change the other copies – they are all individual. With classes, each copy of an object points at the same original object, so if you change one they all change. Swift calls structs "value types" because they just point at a value, and classes "reference types" because objects are just shared references to the real value. This is an important difference, and it means the choice between structs and classes is an important one: • If you want to have one shared state that gets passed around and modified in place, you're looking for classes. You can pass them into functions or store them in arrays, modify them in there, and have that change reflected in the rest of your program.
www.hackingwithswift.com
79
• If you want to avoid shared state where one copy can't affect all the others, you're looking for structs. You can pass them into functions or store them in arrays, modify them in there, and they won't change wherever else they are referenced. If I were to summarise this key difference between structs and classes, I'd say this: classes offer more flexibility, whereas structs offer more safety. As a basic rule, you should always use structs until you have a reason to use classes.
www.hackingwithswift.com
80
Properties Structs and classes (collectively: "types") can have their own variables and constants, and these are called properties. These let you attach values to your types to represent them uniquely, but because types can also have methods you can have them behave according to their own data. Let's take a look at an example now:
struct Person { var clothes: String var shoes: String
func describe() { print("I like wearing \(clothes) with \(shoes)") } }
let taylor = Person(clothes: "T-shirts", shoes: "sneakers") let other = Person(clothes: "short skirts", shoes: "high heels") taylor.describe() other.describe()
As you can see, when you use a property inside a method it will automatically use the value that belongs to the same object.
Property observers Swift lets you add code to be run when a property is about to be changed or has been changed. This is frequently a good way to have a user interface update when a value
www.hackingwithswift.com
81
changes, for example. There are two kinds of property observer: willSet and didSet, and they are called before or after a property is changed. In willSet Swift provides your code with a special value called newValue that contains what the new property value is going to be, and in didSet you are given oldValue to represent the previous value. Let's attach two property observers to the clothes property of a Person struct:
struct Person { var clothes: String { willSet { updateUI("I'm changing from \(clothes) to \(newValue)") }
didSet { updateUI("I just changed from \(oldValue) to \(clothes)") } } }
func updateUI(msg: String) { print(msg) }
var taylor = Person(clothes: "T-shirts") taylor.clothes = "short skirts"
That will print out the messages "I'm changing from T-shirts to short skirts" and "I just changed from T-shirts to short skirts."
www.hackingwithswift.com
82
Computed properties It's possible to make properties that are actually code behind the scenes. We already used the uppercaseString property of strings, for example – that's something that gets calculated as needed, rather than every string always storing an uppercase version of itself. To make a computed property, place an open brace after your property then use either get or set to make an action happen at the appropriate time. For example, if we wanted to add a ageInDogYears property that automatically returned a person's age multiplied by seven, we'd do this:
struct Person { var age: Int
var ageInDogYears: Int { get { return age * 7 } } }
var fan = Person(age: 25) print(fan.ageInDogYears)
Computed properties are common in Apple's code, but less common in user code. Many programmers prefer to use methods because their behavior is clearer.
www.hackingwithswift.com
83
Static properties and methods Swift lets you create properties and methods that belong to a type, rather than to instances of a type. This is helpful for organizing your data meaningfully by storing shared data. Swift calls these shared properties "static properties", and you create one just by using the static keyword. Once that's done, you access the property by using the full name of the type. Here's a simple example:
struct TaylorFan { static var favoriteSong = "Shake it Off"
var name: String var age: Int }
let fan = TaylorFan(name: "James", age: 25) print(TaylorFan.favoriteSong)
So, a Taylor Swift fan has a name and age that belongs to them, but they all have the same favorite song. Because static methods belong to the class rather than to instances of a class, you can't use it to access any non-static properties from the class.
www.hackingwithswift.com
84
Access control This is an important feature and one you need to understand, but sadly it's one that doesn't work in Swift playgrounds so you'll just have to take my word for it. Access control lets you specify what data inside structs and classes should be exposed to the outside world, and you get to choose three modifiers: • Public: this means everyone can read and write the property. • Internal: this means only your Swift code can read and write the property. • Private: this means that only Swift code in the same file as the type can read and write the property. Most of the time you don't need to specify access control, but sometimes you'll want to explicitly set a property to be private because it stops others from accessing it directly. This is useful because your own methods can work with that property, but others can't, thus forcing them to go through your code to perform certain actions. To declare a property private, just do this:
class TaylorFan { private var name: String! }
Reminder: this does nothing on Swift playgrounds, because one playground is effectively one file and thus can read and write any data it likes.
www.hackingwithswift.com
85
Polymorphism and type casting Because classes can inherit from each other (e.g. CountrySinger can inherit from Singer) it means one class is effectively a superset of another: class B has all the things A has, with a few extras. This in turn means that you can treat B as type B or as type A, depending on your needs. Confused? Let's try some code:
class Album { var name: String
init(name: String) { self.name = name } }
class StudioAlbum: Album { var studio: String
init(name: String, studio: String) { self.studio = studio super.init(name: name) } }
class LiveAlbum: Album { var location: String
init(name: String, location: String) { self.location = location super.init(name: name)
www.hackingwithswift.com
86
super.init(name: name) } }
That defines three classes: albums, studio albums and live albums, with the latter two both inheriting from Album. Because any instance of LiveAlbum is inherited from Album it can be treated just as either Album or LiveAlbum – it's both at the same time. This is called "polymorphism," but it means you can write code like this:
var taylorSwift = StudioAlbum(name: "Taylor Swift", studio: "The Castles Studios") var fearless = StudioAlbum(name: "Speak Now", studio: "Aimeeland Studio") var iTunesLive = LiveAlbum(name: "iTunes Live from SoHo", location: "New York")
var allAlbums: [Album] = [taylorSwift, fearless, iTunesLive]
There we create an array that holds only albums, but put inside it two studio albums and a live album. This is perfectly fine in Swift because they are all descended from the Album class, so they share the same basic behavior. We can push this a step further to really demonstrate how polymorphism works. Let's add a getPerformance() method to all three classes:
class Album { var name: String
init(name: String) { self.name = name }
www.hackingwithswift.com
87
func getPerformance() -> String { return "The album \(name) sold lots" } }
class StudioAlbum: Album { var studio: String
init(name: String, studio: String) { self.studio = studio super.init(name: name) }
override func getPerformance() -> String { return "The studio album \(name) sold lots" } }
class LiveAlbum: Album { var location: String
init(name: String, location: String) { self.location = location super.init(name: name) }
override func getPerformance() -> String { return "The live album \(name) sold lots" } }
www.hackingwithswift.com
88
The getPerformance() method exists in the Album class, but both child classes override it. When we create an array that holds Albums, we're actually filling it with subclasses of albums: LiveAlbum and StudioAlbum. They go into the array just fine because they inherit from the Album class, but they never lose their original class. So, we could write code like this:
var taylorSwift = StudioAlbum(name: "Taylor Swift", studio: "The Castles Studios") var fearless = StudioAlbum(name: "Speak Now", studio: "Aimeeland Studio") var iTunesLive = LiveAlbum(name: "iTunes Live from SoHo", location: "New York")
var allAlbums: [Album] = [taylorSwift, fearless, iTunesLive]
for album in allAlbums { print(album.getPerformance()) }
That will automatically use the override version of getPerformance() depending on the subclass in question. That's polymorphism in action: an object can work as its class and its parent classes, all at the same time.
Converting types with type casting You will often find you have an object of a certain type, but really you know it's a different type. Sadly, if Swift doesn't know what you know, it won't build your code. So, there's a solution, and it's called type casting: converting an object of one type to another. Chances are you're struggling to think why this might be necessary, but I can give you a very simple example:
www.hackingwithswift.com
89
simple example:
for album in allAlbums { print(album.getPerformance()) }
That was our loop from a few minutes ago. The allAlbums array holds the type Album, but we know that really it's holding one of the subclasses: StudioAlbum or LiveAlbum. Swift doesn't know that, so if you try to write something like print(album.studio) it will refuse to build because only StudioAlbum objects have that property. Type casting in Swift comes in three forms, but most of the time you'll only meet two: as? and as!, known as optional downcasting and forced downcasting. The former means "I think this conversion might be true, but it might fail," and the second means "I know this conversion is true, and I'm happy for my app to crash if I'm wrong." When I say "conversion" I don't mean that the object literally gets transformed. Instead, it's just converting how Swift treats the object – you're telling Swift that an object it thought was type A is actually type E. The question and exclamation marks should give you a hint of what's going on, because this is very similar to optional territory. For example, if you write this:
for album in allAlbums { let studioAlbum = album as? StudioAlbum }
Swift will make studioAlbum have the data type StudioAlbum?. That is, an optional studio album: the conversion might have worked, in which case you have a studio album you can work with, or it might have failed, in which case you have nil. This is most commonly used with if let to automatically unwrap the optional result, like this:
for album in allAlbums { print(album.getPerformance())
www.hackingwithswift.com
90
if let studioAlbum = album as? StudioAlbum { print(studioAlbum.studio) } else if let liveAlbum = album as? LiveAlbum { print(liveAlbum.location) } }
That will go through every album and print its name, because that's common to the Album class and all its subclasses. It then checks whether it can convert the album value into a StudioAlbum, and if it can it prints out the studio name. The same thing is done for the LiveAlbum in the array. Forced downcasting is when you're really sure an object of one type can be treated like a different type, but if you're wrong your program will just crash. Forced downcasting doesn't need to return an optional value, because you're saying the conversion is definitely going to work – if you're wrong, it means you wrote your code wrong. To demonstrate this in a non-crashy way, let's strip out the live album so that we just have studio albums in the array:
var taylorSwift = StudioAlbum(name: "Taylor Swift", studio: "The Castles Studios") var fearless = StudioAlbum(name: "Speak Now", studio: "Aimeeland Studio")
var allAlbums: [Album] = [taylorSwift, fearless]
for album in allAlbums { let studioAlbum = album as! StudioAlbum print(studioAlbum.studio) }
www.hackingwithswift.com
91
That's obviously a contrived example, because if that really were your code you would just change allAlbums so that it had the data type [StudioAlbum]. Still, it shows how forced downcasting works, and the example won't crash because it makes the correct assumptions. Swift lets you downcast as part of the array loop, which in this case would be more efficient. If you wanted to write that forced downcast at the array level, you would write this:
for album in allAlbums as! [StudioAlbum] { print(album.studio) }
That no longer needs to downcast every item inside the loop, because it happens when the loop begins. Again, you had better be correct that all items in the array are StudioAlbums, otherwise your code will crash. Swift also allows optional downcasting at the array level, although it's a bit more tricksy because you need to use the nil coalescing operator to ensure there's always a value for the loop. Here's an example:
for album in allAlbums as? [LiveAlbum] ?? [LiveAlbum]() { print(album.location) }
What that means is, "try to convert allAlbums to be an array of LiveAlbum objects, but if that fails just create an empty of live albums and use that instead" – i.e., do nothing. It's possible to use this, but I'm not sure you'd really want to!
www.hackingwithswift.com
92
Closures You've met integers, strings, doubles, floats, Booleans, arrays, dictionaries, structs and classes so far, but there's another type of data that is used extensively in Swift, and it's called a closure. These are complicated, but they are so powerful and expressive that they are used pervasively in Cocoa Touch, so you won't get very far without understanding them. A closure can be thought of as a variable that holds code. So, where an integer holds 0 or 500, a closure holds lines of Swift code. It's different to a function, though, because closures are a data type in their own right: you can pass a closure as a parameter or store it as a property. Closures also capture the environment where they are created, which means they take a copy of the values that are used inside them. You never need to design your own closures so don't be afraid if you find the following quite complicated. However, Cocoa Touch will often ask you to write closures to match its needs, so you at least need to know how they work. Let's take a Cocoa Touch example first:
let vw = UIView()
UIView.animateWithDuration(0.5, animations: { vw.alpha = 0 })
UIView is a data type in UIKit that represents the most basic kind of user interface container. Don't worry about what it does for now, all that matters is that it's the basic user interface component. UIView has a method called animateWithDuration() and it lets you change the way your interface looks using animation – you describe what's changing and over how many seconds, and Cocoa Touch does the rest. The animateWithDuration() method takes two parameters in that code: the number of seconds to animate over, and a closure containing the code to be executed as part of the animation. I've specified half a second as the first parameter, and for the second I've asked UIKit to adjust the view's alpha (that's opacity) to 0, which means "completely transparent." This method needs to use a closure because UIKit has to do all sorts of work to prepare for the animation to begin, so what happens is that UIKIt takes a copy of the code inside the
www.hackingwithswift.com
93
braces (that's our closure), stores it away, does all its prep work, then runs our code when it's ready. This wouldn't be possible if we just run our code directly. The above code also shows how closure's capture their environment: I declared the vw constant outside of the closure, then used it inside. Swift detects this, and makes that data available inside the closure too. Swift's system of automatically capturing a closure's environment is very helpful, but can occasionally trip you up: if object A stores a closure as a property, and that property also references object A, you have something called a strong reference cycle and you'll have unhappy users. This is a substantially more advanced topic than you need to know right now, so don't worry too much about it just yet.
Trailing closures As closures are used so frequently, Swift can apply a little syntactic sugar to make your code easier to read. The rule is this: if the last parameter to a method takes a closure, you can eliminate that parameter and instead provide it as a block of code. For example, we could convert the previous code to this:
let vw = UIView()
UIView.animateWithDuration(0.5) { vw.alpha = 0 }
It does make your code shorter and easier to read, so this syntax form – known as trailing closure syntax – is preferred.
www.hackingwithswift.com
94
Wrap up That's the end of our tour around the Swift programming language. I haven't tried to cover everything in the language, but that's OK because you have all the important stuff, all the sometimes-important stuff, and all the nice-to-know stuff – the interesting-but-rarely-used features you'll either come across in a later project or through extended experience with the language. If you find yourself a bit confused about how exactly some features are used, don't worry: as soon as you start coding apps with Swift things will start to make a lot more sense! At this point, you have two options: start on Project 1 of Hacking with Swift, where you make your initial iOS app and recap some of the basic features of Swift, or if you're really desperate to learn more about the language I have four articles that teach you the new features of Swift 2.0 by example.
www.hackingwithswift.com
95
Project 1 Storm Viewer Get started coding in Swift by making an image viewer app and learning key concepts.
www.hackingwithswift.com
96
Setting up In this project you'll produce an application that lets users scroll through a list of images, then select one to view. It's deliberately simple, because there are many other things you'll need to learn along the way, so strap yourself in – this is going to be long! Launch Xcode, and choose "Create a new project" from the welcome screen. Choose Master-Detail Application from the list and click Next. For Product Name enter Project1, then make sure you have Swift selected for language and Universal for devices.
Creating a new Master-Detail Application project in Xcode.
One of the fields you'll be asked for is "Organisation Identifier", which is a unique identifier usually made up of your personal web site domain name in reverse. For example, I would use com.hackingwithswift if I were making an app. You'll need to put something valid in there if you're deploying to devices, but otherwise you can just use com.example.
www.hackingwithswift.com
97
Setting your Organization Identifier in Xcode.
Important note: some of Xcode's project templates have checkboxes saying "Use Core Data", "Include Unit Tests" and "Include UI Tests". Please ensure these boxes are unchecked for this project and indeed all projects in this series. Now click Next again and you'll be asked where you want to save the project – your desktop is fine. Once that's done, you'll be presented with the example project that Xcode made for you. The first thing we need to do is make sure you have everything set up correctly, and that means running the project as-is. When you run a project, you get to choose what kind of device the iOS Simulator should pretend to be, or you can also select a physical device if you have one plugged in. These options are listed under the Product > Destination menu, and you should see iPad 2, iPad Air, iPhone 6, and so on. There's also a shortcut for this menu: at the top-left of Xcode's window is the play and stop button, but to the right of that it should say Project1 then a device name. You can click on that device name to select a different device.
www.hackingwithswift.com
98
For now, please choose iPhone 5s, and click the Play triangle button in the top-left corner. This will compile your code, which is the process of converting it to instructions that iPhones can understand, then launch the simulator and run the app. As you'll see when you interact with the app, this basic project adds dates to the table when you click the + button, you can delete them either by swiping or using the Edit button, and tapping on a date brings in a new screen showing the date in its center.
The basic Master-Detail Application project in Xcode.
You'll be starting and stopping projects a lot as you learn, so there are three basic tips you need to know: • You can run your project by pressing Cmd+R. This is equivalent to clicking the play button. • You can stop a running project by pressing Cmd+. • If you have made changes to a running project, just press Cmd+R again. Xcode will prompt you to stop the current run before starting another. Make sure you check the "Do not show this message again" box to avoid being bothered in the future.
www.hackingwithswift.com
99
This project is all about letting users select images to view, so you're going to need to import some pictures. Download the files for this project from GitHub, and look in the Project1 folder. You'll see another folder in there called Project1, and inside that a folder called Content. I want you to drag that Content folder straight into your Xcode project, just under where it says "Info.plist". A window will appear asking how you want to add the files: make sure "Copy items if needed" is checked, and "Create groups" is selected. Important: do not choose "Create folder references" otherwise your project will not work.
When you add items to Xcode, make sure you choose Create Folder References.
Click Finish and you'll see a yellow Content folder appear in Xcode. If you see a blue one, you didn't select "Create groups", and you'll have problems following this tutorial!
www.hackingwithswift.com
100
Deleting skeleton code Apple's example contains lots of code we don't need, so let's zap it: select the file MasterViewController.swift to open it for editing. Around line 17 you'll see the code starting with override func viewDidLoad() {, and there'll be some more lines of code until it reaches a } on a line all by itself on line 28. If you're not sure which } I mean, it's the one that is aligned directly beneath the first letter in override. No line numbers? If your Xcode isn't showing line numbers by default, I suggest you turn them on. Go to the Xcode menu and choose Preferences, then choose the Text Editing tab and make sure "Line numbers" is checked. This block of code is the viewDidLoad() method, which is code that gets called when the system has finished creating the screen and is giving you the chance to configure it. The method starts at the func viewDidLoad() { line and ends on the } not far below. These symbols, known as braces (or sometimes curly brackets) are used to mark chunks of code, and it's convention to indent lines inside braces so that it's easy to identify where code blocks start and end. But enough of the theory: almost everything inside this method is not needed, so delete its contents except for the line super.viewDidLoad(). Note: when I say "delete its contents" I mean leave the func viewDidLoad() { and } intact, but remove everything in between except for that one line. So, it should look like this:
override func viewDidLoad() { super.viewDidLoad() }
This method does nothing now, but that's OK: we'll be filling it in later. Next up, look for the insertNewObject() method, which starts with func insertNewObject() { and ends with a closing brace a few lines later. Delete the entire method – yes, even the func insertNewObject(sender: AnyObject) { and } parts. These methods start with func, which is short for "function", which for nearly all intents and purposes is identical to a method in Swift. There is one small exception, but you won't come
www.hackingwithswift.com
101
to it until project 24 so for now please just consider functions and methods identical. Finally, delete the very last method in the file. This one has a very peculiar name, and it's a bit of a hangover from Apple's previous language, Objective C. The method is quite long, and although we don't need it here, you do need to understand what it means. Here's the method definition:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
0 thoughts to “Hacking with swift pdf download free”