Swift, swiftly

61
Swift, Swiftly Jack Nutting @jacknutting My name is Jack, I’m an iOS developer at thoughtbot, and today I’m going to talk about ->

Transcript of Swift, swiftly

Swift, Swiftly

Jack Nutting @jacknutting

My name is Jack, I’m an iOS developer at thoughtbot, and today I’m going to talk about ->

SwiftWhat it is, why it exists, and why you should care

We’re going to talk about (these things), in no particular order.

“Objective-C without the C”

This is what Apple said when they announced Swift.Wouldn’t that be SmallTalk?

is a new application programming language.

Swift…

Can also be used for system programming, but primarily application programming

does not derive from C.

Swift…

It includes practically all the features of C, but is not source-compatible in any way.

compiles to native machine code.

Swift…

There’s nothing like the JVM or CLR underlying this.However it does use the same dynamic runtime that gives Objective-C much of its power.

is a complement to Objective-C.

Swift…

Complement or alternative.It’s possible that it will replace Objective-C some day, but that is many years away.Apple has *lots* of Objective-C code.

Swift objects are first-class citizens

first-class citizens in the Cocoa world.

Swift classes can inherit from Objective-C classes

And they often will. But, they don’t have to.However, if they don’t at least inherit from NSObject, the base class in Objective-C, they may have a hard time playing well with Objective-C APIs.

New Syntax

One of the primary features of Swift is a new syntax for calling swift functions and methods, as well as using existing C and Objective-C code

// objc: [thing1 methodName:thing2];

// swift: thing1.methodName(thing2)

Note the elimination of square brackets for method calls.

// objc: [task executeWithFoo:myFoo bar:myBar];

// swift: task.executeWithFoo(myFoo, bar:myBar)

When calling a method that takes multiple parameters, the multiple parts of the method name are now used as method name and “external parameter names”.We’ll get back to the syntax a little later.

Limitations

Proprietary

• Mac OS X only

• Xcode 6 and up

• Deploy to iOS 7+, OS X 10.9+

Does not play well with C++

If you need to interact with C++, you’ll have to write your own bridging code in C or Objective-C.

No open-source implementations

(yet)

no open-source versions —> (yet)Aral Balkan and Greg Casamento are working on one called Phoenix.Part of this initiative seems to be about goading Apple into open-sourcing Swift.

Swift Language Details

Basic typesInt, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64 Float, Float32, Float64 String Array Dictionary Tuple

Swift has a huge list of specific integer types, and a few different float types.Probably just use Int and Float most of the time.Also built-in String, Array, and Dictionary types. (which we’ll get back to later)Finally tuple. If you’re familiar with tuples in ruby etc, this is similar.

Variables vs Constantsvar helloWorld : String = "Hello" helloWorld.extend(", World") helloWorld = "Goodbye"

let hello : String = "Hello" hello.extend(", World") // error hello = "Hello, World" // error

Use “var” to create a variable.Using “let” creates a constant. Can’t be mutated, can’t be reassigned. In Swift, notions of “constant” and “immutable” are in lock-step.Immutability helps the compiler reason about your code, and therefore optimize it. Anyone familiar with functional programming will probably appreciate this.

Type Inference

// Instead of this: var f : Float = 23.5

// just do this: var f = 23.5

Swift is strongly-typed, but type inference can tighten up the code.Any time the compiler knows the type that an expression evaluates to, you have the chance to skip specifying the type.

Forcing it// Given: var f = 23.5 // f is a Float

// This doesn’t even work: var i : Int = f

// But this does: var i : Int = Int(f)

// Even better: var i = Int(f)

Side note. You can’t directly assign some things that would work fine in C. But you can force it, and type inference can help tighten up the code in the end.

Functionsfunc add(n1 : Int, n2 : Int) -> Int { return n1 + n2 }

var result = add(23, 42)

(<list of param types>) -> <return type>

The return type, instead of being on the left, is pushed off to the right, after an arrow.You’ll see this pattern repeated later.-> It looks like this.

Closures

var add = {(n1: Int, n2: Int) -> Int in return n1 + n2 }

var result = add(23, 42)

(<list of param types>) -> <return type>

Creating a closure is very simple. Especially compared to blocks in Objective-C, which are basically equivalent.We create a closure, and call it just like a function.The big diff, not shown here, is that a closure can capture values from its scope.-> There’s that pattern again.

Genericsfunc add(n1: Int, n2: Int) -> Int { return n1 + n2 }

add(UInt8(23), UInt8(123)) // Error!

Our previous add function was really too specific. If we try to use it with anything besides plain Ints, it won’t compile

Genericsfunc add(n1: Int, n2: Int) -> Int { return n1 + n2 }

func add<T: IntegerType>(n1: T, n2: T) -> T { return n1 + n2 }

add(UInt8(23), UInt8(123)) // OK!

With generics, we can solve this by creating a new function that declares a generic type, here called T, to be used for the parameters and return value.In this case, we’re declaring that the type T can’t be just *anything*, it has to be a type that conforms to the IntegerType protocol. (we’ll get to protocols a little later).OK to have 2 “add” functions. The one that most specifically matches the parameter types gets used.

Arrays

var a1 = [String]() var a2 = ["How", "you", "doin'"]

Arrays in Swift are type-safe. You can create an empty array by specifying the type of its items in square brackets, followed by parens to call the array initializer.You can create a non-empty array just by listing its contents.

Arrays

for name in ["Garfunkel", "Oates"] { println("How about some \(name)") }

Array iteration is very simple. Note the type inference again.Here you also see a use of the println function for outputting some text, and you see the use of Swift’s built-in string interpolation. Anything inside backslash-prefixed parens is evaluated as an expression.

Dictionaries

var d1 = [String : String]() var d2 = ["foo" : "FOO", "bar" : "BAR"]

Dictionaries are also type-safe, and are created similarly to arrays.

Dictionaries

let d = ["answer" : “42", "question" : "UNKNOWN"] for (key, value) in d { println("The \(key) is \(value)") }

Dictionary iteration is also a piece of cake.

Tuples

var t = (23, 42, 47) var i = t.0 // 23 t.2 = 100 // (23, 42, 100)

Tuples are similar to tuples in many other language, and can be useful when creating a function that needs to return multiple values.

Classes

class Thing: NSObject, NSCopying { }

Declaring a class is simple. For starters, you just name it, specify its parent class (if any) and any protocols it should conform to.Building iOS or OS X apps, you will almost always have a parent class.Like in Objective-C, Swift classes can only inherit from a single parent class, but can inherit interfaces using protocols. (We’ll get to protocols soon)

Classes

class Thing { var foo : Int = 2 let bar : Int = 3 }

Frequently, classes will have properties. You can give them initial values when declaring them, or… ->

Classesclass Thing { var foo : Int let bar : Int init() { foo = 2 bar = 3 } }

Or you can set things up in an initializer, like this. Like Objective-C, each class can have multiple initialisers. In Swift, you can specify that some initialisers are required and others are not, which affects what subclasses must implement. This actually gets a little complicated, so in the interest of time, we’ll not dig further into init() topics today.

Classesclass Thing { var foo : Int = 2 let bar : Int = 3 var fooPlusBar : Int { return foo + bar } }

let sum = myThing.fooPlusBar

You can also define a property with an inline code block. This is then callable with dot-syntax, with no parentheses, like any other property.A property created this way has no underlying value of its own, and is called a “computed property”

Classesclass Thing { var foo : Int = 2 let bar : Int = 3 var fooPlusBar : Int { get { return foo + bar } set { foo = newValue - bar } } }

myThing.fooPlusBar = 20

You can also provide both a getter and a setter for a computed property, like this.

Classes

class Thing { […] func fooTimesBar () -> Int { return foo * bar } }

let product = myThing.fooTimesBar()

(<list of param types>) -> <return type>

And of course, you can define methods for your classes. Also callable with dot-syntax, must have parentheses (even when calling a method with no parameters). -> BTW, there’s that same pattern we’ve seen elsewhere. There’s a nice sort of symmetry between the syntaxes for functions, methods, and closures.

Extensions

extension String { var uppercase: String { return self.uppercaseString } }

“hello".uppercase // “HELLO”

In Objective-C we have categories that let you add methods and properties to any class, including ones you don’t have the source for. In Swift, that same feature is called an Extension. Unlike Obj-C categories, Swift extensions don’t each have a name of their own.

Protocols

protocol FullyNamed { var fullName: String { get } }

Swift’s Protocols are just like in Objective-C. They are simple lists of methods and properties, which must be implemented in anything that wants to conform to that protocol. Also just like Objective-C, Protocols can inherit from other protocols.

Structs

struct Rectangle { var x : CGFloat var y : CGFloat var width : CGFloat var height : CGFloat }

var r = Rectangle(x: 500, y: 500, width: 100, height: 100)

Structs look pretty similar to classes. One big difference, compared to a class, is that each struct has an initialiser created for it automatically.

Structsstruct Rectangle { […] var area : CGFloat { return width * height; } mutating func shift(dx : CGFloat, dy: CGFloat) { x += dx y += dy } }

r.shift(40, 0)

In Swift, structs are much richer than their C counterparts. The elements they contain are actually properties, and can be set up just like in classes. Also methods, protocols, and extensions. Any methods that modify a struct must be marked with the “mutating” keyword.No inheritance.Pass-by-value (like primitive types) instead of pass-by-reference (like objects).

Enumerations

enum ButtonType { case Square case RoundedCorners case Circle }

let b = ButtonType.Square

Enumerations are similar to their C equivalent. You can use one to select from one of several options.

Enumerationsenum DateComponent { case Year(Int) case Month(Int) case Day(Int) case TimeOfDay(Int, Int) }

let thisYear = DateComponent.Year(2014) let thisMonth = DateComponent.Month(11) let todaysDate = DateComponent.Day(5) let startTime = DateComponent.TimeOfDay(10, 20)

Also, each of those can have one or several associated values. The choice you make in an enum can contain a piece of data.Enumerations can also have methods, protocols, and extensions, just like classes.No inheritance.Pass-by-value.

Optionals

enum Optional<T> { case None case Some(T) }

This point is really a head-scratcher for many people at first.Similar to the “maybe monad” in Haskell.An optional can either contain some value, or contain none at all. If you were to define it in code, it might look something like this.

Optionals

?

But you don’t need to define it, it’s already there and deeply embedded in the language. Any type that is followed immediately by a question-mark becomes an optional-wrapped value. What is the use of this?

Optionalsvar s1 : String countElements(s1) // won’t compile

var s2 : String? countElements(s2) // won’t compile

if let realS2 = s2 { countElements(realS2) }

First example won’t compile because s1 is not allowed to be used uninitialized. It can’t be nil.Second example, s2 is an optional and is therefore allowed to be nil, but this won’t compile because s2 is not actually a String; it’s an Optional-wrapped string.Third shows unwrapping the string into an actual String

Optionals

person?.employer?.office?.deliver(parcel)

You can shortcut some of the reassignments by using question marks. Traversing a hierarchy where some values may be nil suddenly gets easy. This line of code will simply “bail” if person is nil, or if the values returned by the employer property or its office property are nil. Lets you skip a lot of nested ifs.

Implicitly Unwrapped Optionalsclass LateBloomer { var name : String? func lateSetup(name : String) { // this method is called // shortly after init self.name = name } }

let lb = LateBloomer() lb.lateSetup("foo") println("Name: \(lb.name)")

"Name: Optional("foo")"

Sometimes you need to use an optional for a property that can’t be populated during init, but that you know will be populated later.Here name is an optional, and accessing its value always requires unwrapping. During the final string interpolation here, grabbing the value of “name” reveals its true nature.

Implicitly Unwrapped Optionalsclass LateBloomer { var name : String! func lateSetup(name : String) { // this method is called // shortly after init self.name = name } }

let lb = LateBloomer() lb.lateSetup("foo") println("Name: \(lb.name)")

"Name: foo"

Change ? to ! in the declaration for “name” to make this implicitly unwrapped. Now you can access the value inside the optional directly, as if it were non-optional. Note that if the value really contains a nil and you try to access it this way, you will generate a runtime error and crash your app. So only do this if you really know you’re right.

Switchlet someCharacter: Character = "e" switch someCharacter { case "a", "e", "i", "o", "u": println("\(someCharacter) is a vowel") case "b", "c", "d", "f", "g", "h", "j", "k", "l", “m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": println("\(someCharacter) is a consonant") default: println("\(someCharacter) is not a vowel or a consonant") }

Swift’s switch construct is much more flexible and useful than C’s.Each case can match multiple items.No falling through from one case to next (no need to “break”)

Switchlet count = 10000 let countedThings = "raindrops on the ground" var naturalCount: String switch count { case 0: naturalCount = "no" case 1...3: naturalCount = "a few" case 4...9: naturalCount = "several" default: naturalCount = "far too many" } println("There are \(naturalCount) \(countedThings).")

Each case can also contain a range.

Switchlet somePoint = (1, 1) switch somePoint { case (0, 0): println("(0, 0) is at the origin") case (_, 0): println("(\(somePoint.0), 0) is on the x-axis") case (0, _): println("(0, \(somePoint.1)) is on the y-axis") default: println("(\(somePoint.0), \(somePoint.1)) is on no axis") }

Each case can match against contents in a tuple.And more!

Type Casting// Examining an object var object : AnyObject = "Hi there" if object is String { println(object) }

// Safe typecast if let string = object as? String { println(string) }

First example just checks to see if “object” is a string, and then uses it directly as such.Second example copies the value, if appropriate, into a new constant with the correct type.Side note: Here we’re using AnyObject, which represents any object (like “id” in ObjC). Similarly, “Any” represents any value at all, including structs, enums, primitives.

Integration with Xcode

Xcode lets you mix and match Swift and Objective-C.

Bridging Header// // Use this file to import your // target's public headers that you // would like to expose to Swift. //

#import "PluginProtocol.h"

When you add your first swift file to an existing objc project, Xcode prompts for creation of bridging header. Say yes!Here’s an example.When you build, Xcode makes a new header where you can import any ObjC headers that you want to be accessible from Swift. Use this header to import any Objective-C code in your project that you need to access from Swift.

Bridged Types

NSString NSArray NSDictionary NSNumber NSInteger, NSUInteger

String Array Dictionary Int, Float Int

Some Objective-C types are automatically bridged to swift equivalents.From Swift’s point of view, all NSArray and NSDictionary instances are bridged to contain values typed to AnyObject, so you may need to cast things later.

NS_ENUMtypedef NS_ENUM(NSInteger, UITableViewCellStyle) { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle };

enum UITableViewCellStyle: Int { case Default case Value1 case Value2 case Subtitle }

Existing types declared using NS_ENUM automatically get nice Swift translations

respondsToSelector:

// Objective-C: if ([delegate respondsToSelector:@selector(foo)]) { [delegate foo]; }

// Swift: delegate?.foo?()

Swift has a nice shortcut for respondsToSelector:First ? is in case the optional delegate is nil, second is checking to see if the method exists.Technically, this only works when checking for “optional” methods declared in a protocol that the receiver conforms to. For the more general case of asking any object about any method, you’ll still have to use respondsToSelector:

Playgrounds

Swift REPL in a GUI, with graphics results on the side.Playgrounds let you try out pieces of code without needing to set up a real project.Code you write in a playground is in its own little world, needs to be copy-pasted elsewhere if you want to reuse it.

Jack Nutting @jacknutting