Swift is an exciting new language from Apple, first announced at the Apple Worldwide Developers Conference (WWDC) in June 2014. The language started life as the brainchild of Chris Lattner, director of Apple’s Developer Tools department, and is the next step in the evolution of Apple’s software development ecosystem.
Swift brings with it many modern language features, including type safety, generics, type inference, closures, tuples, protocols, automatic memory management, and support for Unicode.
Swift as a Scripting Language
You can use Swift as a scripting language, much like Perl, Python, or Ruby. To use Swift in this manner, ensure the first line of the script contains the path to the Swift "interpreter". If you want to try using Swift this way, type the following into a text file named hello.swift:
#!/usr/bin/swift print ("Hello, World")
Next, ensure the script is marked as executable with a chmod
command:
chmod u+x hello.swift
Now, run the script as follows:
./hello.swift
Swift will compile your program, and assuming there are no syntax errors, will execute it.
Variables and Constants
Variables and constants must be declared before you use them. You declare variables by using the var
keyword, followed by the variable’s name, a colon, and then its type, as shown here:
var name: Type var anInt: Int var aStr: String var aChar: Character
You can assign values to variables at the same time you declare them:
var anotherInt: Int = 45 var anotherStr: String = "Frodo"
You declare constants by using the let
keyword. They look like variables because of the way they are created and used, but they are immutable - meaning they cannot be changed. Because a constant cannot be changed, it must be assigned a value when it is declared.
let name: Type = expr let constFloat: Float = 23.1 let constStr: String = "Bilbo"
Computed Variables
A computed variable is not a variable in the usual sense - it is not a value that is stored in memory and read whenever it is referenced in an expression or during assignment. Instead, computed variables are functions that look like variables.
A computed variable contains two functions: a getter (identified with the keyword get
, which returns the computed value) and a setter (identified with the keyword set
, which might initialize the conditions that affect the value returned by the getter). The declaration looks as follows:
var variableName: someType { get { // code that computes and returns // a value of someType } set(valueName) { // code that sets up conditions // using valueName } }
The valueName
is optional; you use it inside the code that implements the setter to refer to the value passed into the set
method. If you omit it, the parameter can be referred to using the default name of newValue
.
The setter is optional, and for most practical uses, you would not use it. If you don’t use the setter, the get
clause is not required, and all that is required is code to compute and return a value.
var variableName: someType { // code that computes and returns a value }
When a computed variable is defined, it is used exactly like any other variable. If its name is used in an expression, the getter is called. If it is assigned a value, the setter is called:
var badPi: Float { return 22.0/7.0 } let radius: Float = 1.5 let circumference = 2.0 * badPi * radius
Variable Observers
Variable observers are functions (or methods) you can attach to variables and that are called when the value of the variable is about to change (identified with the willSet
keyword) or after it has changed (identified with the didSet
keyword). The declaration looks as follows:
var variableName: someType = expression { willSet(valueName) { // code called before the value is changed } didSet(valueName) { // code called after the value is changed } }
Both valueName
identifiers (and their enclosing parentheses) are optional.
var watcher: Int = 0 { willSet { print("watcher will be changed to", newValue) } didSet { print("watcher was changed from", oldValue) } }
Here is an example of using didSet
to ensure an integer variable can only have an even value:
var onlyEven: Int = 0 { didSet { if ((onlyEven & 1) == 1) { onlyEven++ } } }
Tuples
A tuple is a group of values you can treat as a single entity. Tuples are enclosed in parentheses, with each element separated by a comma.
Tuple examples:
(4, 5) // A tuple with two integer parts ("Hello", 2, 1) // A tuple with a string part and two integer parts
To create a variable or constant that stores a tuple, you list the tuple’s component types inside parentheses where you would usually specify the type, as shown in the following:
var a: (String, Int) = ("Age", 6) let fullName: (String, String) = ("Bill", "Jackson")
Because Swift uses type inferencing, the tuple type can be inferred if the variable or constant is initialized when it is declared. In the following example, there is no need to specify that the tuple’s type is (String
, Int
, String
), because it is inferred by the compiler:
var loco = ("Flying Scotsman", 4472, "4-6-2")
Much like arrays, you can access tuple components by position, with the first component having an index of 0:
let name = loco.0 let number = loco.1
You can name tuple components and then access them by those names. This example names the first component of the tuple name and the second component age:
var person: (name: String, age: Int) person.name = "Fred" person.age = 21 let c = person.age let result = (errCode: 56, errMessage:"file not found") var s = result.errMessage
You can use type aliases to associate a type identifier with a tuple type, and that alias can then be used to create new instances of that tuple type:
typealias locoDetail = (name: String, number: Int, configuration: String) var thomas: locoDetail = ("Thomas", 1, "0-6-0")
Arrays
An array is a collection of items of the same type, be it a simple type (such as Int
, Double
, or String
) or a more complex type (such as a class or structure).
The type of an array is formally specified as Array<Type>
, although the more frequently used shorthand equivalent is [Type]
. Thus, if you see the term [String]
, you can conclude that it means an array of type String
.
You declare arrays in a similar way as variables and constants. You create empty arrays as follows:
var arrayName = [Type]() var daysOfWeek = [String]()
You can initialize them by using an array literal:
var locos: [String] = ["Puffing Billy", "Thomas"] let daysPerMonth: [Int] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 31, 31] var primes = [1, 3, 5, 7, 11]
You access specific array elements by using C-style subscript syntax. The first element in an array has an index of 0. You can access a subset of an array’s elements by using a range. This operation returns a new slice:
let daysPerNorthernSummerMonth = daysPerMonth[5...7] let days = daysPerMonth[5]
Other properties and functions
arrayName.first
- Returns the first element in the array.arrayName.last
- Returns the last element in the array.arrayName.count
- Returns the number of elements in the array.arrayName.isEmpty
- Returns true, if the array has no elements.arrayName.append(value)
- Adds a new element to the end of the array.arrayName.contains()
- Returns a Bool
value that indicates if a specific value is contained in the array.arrayName.filter()
- Returns a new array that contains only the elements that match some condition, which is defined by using a closure. This example filters names longer than four characters: names.filter { $0.characters.count > 4 }
.arrayName.forEach()
- Calls the body of the closure on each element of the array, producing similar functionality to for i in arrayName { ... }
. names.forEach { print($0) }
.arrayName.indexOf(someValue)
- Returns an optional integer representing the position of someValue
in the array, or nil
if the value is not present. names.indexOf("John")
arrayName.joinWithSeparator(someString)
- For an array of strings, returns a new string comprising the elements of arrayName
interposed with someString
. names.joinWithSeparator("; ")
.arrayName.map()
- Returns a new array in which each element has been transformed by a mapping function, which is defined by using a closure. This example returns an array in which any string from the original array that does not start with an uppercase "A" is prefixed with an asterisk (*): names.map { $0.hasPrefix("A") ? $0 : "*" + $0 }
To iterate over all elements in an array, use a for - in
loop:
for item in arrayName { ... }
To use both the position and value of items from the array, use the enumerate()
method, as shown here:
for (index, item) in arrayName.enumerate() { ... }
Sorting a collection in Swift is simple:
let myArray = [3, 1, 2] myArray.sorted() // [1, 2, 3]
There are four sort methods: the non-mutating variant sorted(by:)
, and the mutating sort(by:)
, times two for the versions that default to sorting comparable things in ascending order and take no arguments. For the most common case, sorted()
is all you need. And if you want to sort in a different order, just supply a function:
myArray.sorted(by: >)
You can also supply a function if your elements don’t conform to Comparable
but do have a <
operator, like tuples do:
var numberStrings = [(2, "two"), (1, "one"), (3, "three")] numberStrings.sort(by: <)
Or, you can supply a more complicated function if you want to sort by some arbitrary criteria:
let animals = ["elephant", "zebra", "dog"] animals.sorted { lhs, rhs in let l = lhs.reversed() let r = rhs.reversed() return l.lexicographicallyPrecedes(r) }
Dictionaries
Much like arrays, dictionaries store a collection of values, but whereas array elements are referenced via position, dictionary elements are referenced via unique keys.
A dictionary’s type is formally specified as Dictionary<Key Type, ValueType>
, although [KeyType:ValueType]
is the preferred shorthand equivalent. Thus, if you see the term [String:Int]
, you can assume it means a dictionary whose key type is String
and whose value type is Int
.
Dictionaries are declared in a similar way to variables and constants. You can create empty dictionaries like so:
var dictionaryName = [Type: Type]() var cpus = [String: String]() var cpus2: [String:String] = [:]
Or you can initialize them upon declaration by using a dictionary literal, as demonstrated here:
var cpus: [String:String] = ["BBC Model B":"6502", "Lisa":"68000", "TRS-80":"Z80"]
The type of both the key and the value can be inferred when initialized with a dictionary literal, so the previous example can be reduced to the following:
var cpus = ["BBC Model B":"6502", "Lisa":"68000", "TRS-80":"Z80"]
To access dictionary values, you use the key as a subscript, as illustrated here:
let cpu = cpus["BBC Model B"]
You can use the following features to access various properties of a dictionary:
dictionaryName.count
- Returns the number of key-value pairs in the dictionary.dictionaryName.isEmpty
- Returns true, if the dictionary has no elements.dictionaryName.keys
- Returns an array of all keys in the dictionary, which you can use for iterating over those keysdictionaryName.values
- Returns an array of all values in the dictionary, which can be used for iterating over those valuesTo iterate over all elements in a dictionary, you use a for - in
loop, as follows:
for (key, value) in dictionaryName { ... }
To iterate over just the keys or values of the dictionary, use the keys
or values
property, which returns an array:
for value in dictionaryName.values { ... } for key in dictionaryName.keys { ... }
Sets
A set is an unordered collection of unique values of the same type, which can be a simple type (such as Int
, Double
, or String
) or a more complex type (such as a class or structure). Sets were first introduced in Swift 1.2.
You declare sets in a similar way to variables and constants. You create empty sets as follows:
var setName = Set<Type>() var daysOfWeek = Set<String>()
You can initialize a set using an array literal:
var chessPieces: Set = ["King", "Queen", "Rook", "Bishop", "Knight", "Pawn"] var mySet = Set(["one", "two", "three"])
You can use the following features to access the set and its properties:
someSet.contains(someValue)
- Returns true, if someValue
is a member of the set.someSet.count
- Returns the number of items in the set as an integer.someSet.isEmpty
- Returns true if the set contains no items.setA.exclusiveOrInPlace(setB)
- Replaces the contents of setA
with the items that are unique to setA
and unique to setB
(the "opposite" of the intersection of the sets).setA.intersectInPlace(setB)
- Removes from setA
all items that are not also present in setB
(setA
becomes the intersection of setA and setB ).setA.subtractInPlace(setB)
- Removes any items that exist in both setA
and setB
from setA
.setA.unionInPlace(setB)
- Inserts into setA
all of the items in setB
that are not already present in setA
.setA.exclusiveOr(setB)
- Returns a new set that contains only the items that are unique to setA
and unique to setB
(the "opposite" of the intersection
of the sets).setA.intersect(setB)
- Returns a new set that contains the items that are common to both setA
and setB
.setA.isDisjointWith(setB)
- Returns a Bool
value that indicates if the intersection of setA
and setB
is an empty set (i.e., that they do not have any elements in common).setA.isStrictSubsetOf(setB)
- Returns a Bool
value that indicates if all of the items in setA
are also present in setB
, and that setB
contains elements that are not present in setA
.setA.isStrictSupersetOf(setB)
- Returns a Bool
value that indicates if all of the items in setB
are also present in setA
, and that setA
contains elements that are not present in setB
.setA.isSubsetOf(setB)
- Returns a Bool
value that indicates if all of the items in setA
are also present in setB
.setA.isSupersetOf(setB)
- Returns a Bool
value that indicates if all of the items in setB
are also present in setA
.setA.subtract(setB)
- Returns a new set that contains only the items in setA
that are not present in setB
.setA.union(setB)
- Returns a new set that contains all of the items in setA
and all of the items in setB
.To iterate over all items in a set, use a for - in
loop:
for item in someSet { ... }
Functions
You declare functions in Swift by using the func
keyword, as shown in the following:
func functionName(parameters) -> returnType { // function body }
By default, function parameters are constant (they cannot be modified in the function body). Variable parameters are created by preceding them in the function declaration with the var
keyword, as shown here:
func someFunc(var s: someType) -> ...
You can use the variable s
as a local, modifiable variable in the function body. Variable parameters are lost after the function returns - you cannot use them to pass values outside the function body.
In-out parameters are created by preceding them in the function declaration with the inout
keyword, like so:
func someFunc(inout i: someType) -> ...
i
becomes an alias for an external variable passed by reference. Modifying i
inside the function modifies the external variable. To call such a function, you must place an ampersand (&
) before a referenced variable’s name in a function call:
var i: Int = 45 someFunc(&i)
You can have a function return more than one value by using a tuple, as in the following example:
func getRange() -> (lower: Int, upper: Int) { ... return (someLowValue, someHighValue) }
To specify a default value for a parameter to a function, follow the parameter’s type specification with an assignment, as in the folowing example:
func addAnotherString (s: String, toString: String = "pine") -> String { ... }
A variadic parameter supports a variable number of input values. You specify a variadic parameter by following the parameter type with an ellipsis (...
), as shown here:
func sumOfInts(numbers: Int...) -> Int { var tot = 0 for i in numbers { tot += i } return tot }
Closures
Closures are functionally similar to blocks in Objective-C and lambdas in languages such as Scheme, C#, and Python.
Closures are anonymous functions that can be passed as arguments to other functions (known as higher-order functions) or returned by other functions. They can refer to variables and parameters in the scope in which they are defined (sometimes called outer variables).
You typically define a closure through a closure expression, which takes the following format:
{ (parameters) -> returnType in statements }
This is how you would call sort()
and provide a closure:
let t1 = names.sort({ (s1: String, s2: String) -> Bool in return s1 < s2 })
For simple closures with a single expression, such as that just demonstrated, you can also omit the return
keyword, which reduces the closure further to the following:
let t3 = names.sort( { s1, s2 in s1 < s2 } )
To sort by string length instead of lexically, modify the comparison operands in the closure to compare the lengths of the two strings being compared:
let t6 = names.sort({ s1, s2 in s1.characters.count < s2.characters.count })
For simple inline closures, having to first name the arguments just so you can subsequently refer to them makes the closure longer than it needs to be. For inline closures, Swift assigns automatic argument names to each parameter by using a dollar sign followed by a position number ($0
, $1
, $2
, etc.).
The sort()
closure examples reduce further still to:
let u1 = names.sort( { $0 < $1 } )
When the last (or only) argument provided to a function is a closure, you can write it as a trailing closure. Trailing closures are written after the parentheses that wrap the function’s arguments:
let v1 = names.sort() { $0 < $1 }
If the function has no other arguments than the closure itself, and you’re using trailing closure syntax, you can omit the empty parentheses, reducing the closure to the simplest variant:
let w1 = names.sort { $0 < $1 }
Optionals
Swift’s optionals provide a way to indicate a value exists without usurping some part of the value’s set of possible states to do so.
In Swift, object references are not pointers and may not normally be set to nil
, unless they are explicitly declared to be optional values. An example of the syntax for such a declaration is as follows:
var str: String?
The question mark, which immediately follows the type, declares that the variable str
is an optional. Its value might exist or it might not. Not having a value is not the same as str
storing an empty string. When a new optional is created in this way, its initial value is set to nil
.
When a variable has been declared to be optional, it must either be used in places where an optional context for that type is allowed, or it must be unwrapped to reveal the underlying value.
For example, you can assign an optional to another optional without issue, as shown here:
var n: String? n = str
However, you cannot assign it to a nonoptional:
var r: String r = str
To access the value stored by an optional, first check if a value exists with an if
statement. If it does exist, use the exclamation mark to force unwrap the optional and access the raw value it stores, as demonstrated here:
if str != nil { // check if the optional has a value r = str! // it does – unwrap it and copy it } else { // the optional had no value }
Optional Binding
Optional binding is a way to test whether an optional has a value, and, if it does, make a scoped non-optional copy, all in one operation.
You can use optional binding with the if
statement (where the scoped non-optional copy is only valid inside the first set of braces) or the guard
statement (where the scoped non-optional copy is valid for the remainder of the scope in which it was created).
Following is the syntax for optional binding with if
:
if let aConst = someOptional { // aConst is now an unwrapped version of someOptional print (aConst) } // aConst is out of scope at this point
Assuming that someOptional
has a value, aConst
holds a nonoptional copy of that value inside the first set of braces of the if statement. Because aConst
is not an optional, its value can be used directly - the unwrapping has been handled in the let
statement.
Similarly, here is the syntax for optional binding with guard
:
guard let aConst = someOptional else { // aConst is not valid here - exit this scope return } // aConst is valid in this scope
With guard
, if the optional can be unwrapped, then the binding remains in place until the scope that contains the guard
statement terminates.
Optional Chaining
When you access an optional, it either has a value or is nil
, and you need to test that the value exists before unwrapping it, as in the following example:
var s: String? if s { // check if the optional has a value var r = s! // it does – do something with it } else { // the optional had no value }
Optional chaining is a facility by which you can query an optional, or something that depends on an optional having a value, without specifically having to test the optional first. You can use optional chaining when accessing class, structure, or enumeration properties, methods, or subscripts using dot syntax.
With optional chaining (and let
binding), you can reduce the code to the following:
if let p = a?.otherClass?.someProperty { print (p) } else { print ("no property available") }
If any optional in the chain returns a nil
value, the entire expression returns nil
.
You can call methods using optional chaining, as follows:
a?.otherClass?.someMethod()
Again, the method call will return nil
if the call failed because some part of the chain returned nil
. Even if the method normally returns a nonoptional value, it will always be returned as an optional when used in an optional chain context.
You can also use optional chaining with subscripts:
a?.otherClass?[1] // returns nil, or prints "getter for [1] called" a?.otherClass?[3] = "Optional chaining is neat"
Loops
Swift provides the standard loop constructs you would expect, including for
, while
, and repeat
- while loop variants.
The for-condition-increment loop is functionally the same as the for loop in C. The loop consists of an initialization phase, a test, an increment, and a set of statements that are executed for each iteration of the loop. Here’s an example:
for initialization; condition; increment { statements }
The most familiar version of this loop would be as follows:
for var i=10; i < 15; i++ { print (i) }
You use the for - in loop to iterate over sequences or collections of things, such as the elements of an array or dictionary, the characters in a string, or a range of numbers. Here’s the general format:
for index in sequence { statements }
In the following example, which iterates over a range of numbers, the loop index variable (i
) takes on the value of the next number in the range each time through the loop:
for i in 3...8 { print (i) }
This next example iterates over the contents of a dictionary. A tuple is used as the loop index, so that for each iteration, you get the next key and its associated value. This example also demonstrates that dictionaries are stored in arbitrary order:
var vehicles = ["bike":2, "trike":3, "car":4, "lorry":18] for (vehicle, wheels) in vehicles { print (vehicle) }
The index value of a for - in
loop can be filtered with a where
clause to exclude specific iterations. The following example skips iterations where the index is evenly divisible by 3:
var out = "" for i in 0...15 where (i % 3) != 0 { if (out != "") { out += ", " } out += String(i) } print (out)
Instead of testing a condition, a for - in
loop can take a case
pattern match, which can include enumeration values and let
variable binding (and an optional where
clause). This example iterates through an array of tupples where the buswidth
member has the value 8:
let processors: [(name: String, buswidth: Int)] = [ ("Z80", 8), ("16032", 16), ("80286", 16), ("6502", 8) ] for case let (name, 8) in processors { print ("the", name, "has a bus width of 8 bits") }
This example iterates through an enumeration for .IPv4 cases, and extracts the associated values for printing:
enum NetworkAddress { case MAC(String) case IPv4(UInt8, UInt8, UInt8, UInt8) } let addresses = [ NetworkAddress.IPv4(192, 168, 0, 1), NetworkAddress.IPv4(8, 8, 8, 8), NetworkAddress.MAC("00:DE:AD:BE:EF:00") ] for case let .IPv4(a, b, c, d) in addresses { print (a, b, c, d, separator:".") }
while
loops test a condition ahead of the loop body; only if the condition evaluates to true is the loop body executed. The general format is as follows:
while condition { statements }
You can use the while loop to replicate the functionality of the for-condition-increment loop, as follows:
var count = 0 while (count < 10) { print (count) count ++ }
The condition is tested before the body of the loop is executed. If it evaluates to false
the first time it is executed, the statements in the body of the loop will never execute.
repeat - while loops test the termination condition at the end of the loop, rather than at the start. This means the statements in the body of the loop are guaranteed to be executed at least once. Loop execution continues until the condition evaluates to false
.
The general format for a repeat - while
loop looks like this:
repeat { statements } while condition
Here is an example:
var t = 0 repeat { print (t) t++ } while (t < 10)
Conditional Execution
There are three statements in Swift that support conditional execution of blocks of code: the if - else
statement, the guard - else
statement, and the switch
statement.
The if
statement tests a condition, and executes a block of code only if that condition evaluates to true.
Here’s the simple form:
if condition { // statements to execute }
You can chain multiple if statements in this way
if condition { print ("shouldn't see this") } else if condition { print ("should see this") }
The guard
statement provides similar functionality to the if statement, but is intended for use in situations where you want to do an early bailout of the current scope if one or more conditions are not met.
The basic structure of the guard
statement in a function is as follows:
func someFunc() { guard condition else { return // exit function } // continue execution // ... }
In this example, return
was used to exit the function.
The following example shows a loop that processes an array of strings to see which can be interpreted as integers:
var input = ["45", "27", "Apple", "3"] for str in input { guard let ageAsInt = Int(str) else { // not an int, so ignore continue } print ("age:", ageAsInt) }
The switch
statement provides an alternative (and more concise) way to express a series of condition (or pattern) tests, which you might otherwise implement by using a chain of if - else
statements.
The basic structure of the statement is as follows:
switch expression { case pattern1: // statements case pattern2: // statements case patternN: // statements default: // statements }
A case pattern can be a tuple. The following example demonstrates a crude class scheduling case statement, in which students in different grades (7–10) and from different houses ("Columbus", "Cook") are scheduled for specific activities on different days of the week:
let year = 9 let house: String = "Columbus" let weekday = "Fri" let record = (house, year, weekday) switch record { case ("Columbus", 7...8, "Mon"): print ("Sports: Cricket") case ("Cook", 7...8, "Mon"): print ("Sports: Netball") case ("Columbus", 9...10, "Tue"): print ("Sports: Football") case ("Cook", 9...10, "Tue"): print ("Sports: Tennis") case (_, 7...8, "Wed"): print ("Music") case (_, 9...10, "Wed"): print ("Theater") case (_, 7...10, "Thu"): print ("Sciences") case (_, 7...10, "Fri"): print ("Humanities") default: print("nothing scheduled or invalid input") }
In this example, the underscore (_
) is used in some case patterns to match all possible values.
You can use a where qualifier to further refine a case
clause in a switch
statement. The following example uses the where
clause with a value bound to the day of the week to match cases in which students are of either house, in year 7, and the day is any day that begins with the letter "T":
switch record { // ... preceding cases case (_, 7, let day) where day.hasPrefix("T"): print ("Home Economics") // subsequent cases... }
You can use an enumeration as the value of a switch
statement that is to be matched against each case clause, as illustrated here:
enum TravelClass { case First, Business, Economy } var thisTicket = TravelClass.First switch thisTicket { case .First: print ("Cost is $800") case .Business: print ("Cost is $550") case .Economy: print ("Cost is $200") }
Deferred execution
Swift 2.0 introduces the defer
statement, which allows you to specify a block of code that will be executed as the current scope exits.
The following example defers execution of a print
statement until the end of the do
scope:
do { defer { print ("Goodbye") } print ("Hello") } // prints: // Hello // Goodbye
defer
is useful for placing clean-up code near set-up code, clarifying intent. For example, a function that opens a file can ensure the method for closing that file is always executed, regardless of how the function returns, by wrapping it in a defer
statement at the same place in the code that the file is opened:
func parseFile() { let handle = openSomeFile() defer { closeSomeFile() } // code to parse file return }
Classes
A class is a flexible entity that encapsulates properties (or data) and the methods that operate on them. You can derive classes from other classes (a feature called inheritance), in which case the derived class is referred to as the subclass, and the class from which it is derived is referred to as the superclass.
You declare a base class by using the following syntax:
class ClassName { // property, member and related definitions }
Here is an example of a simple base class that could be used to store a description of a microprocessor:
class Processor { var dataWidth = 0 var addressWidth = 0 var registers = 0 var name = "" }
The four variables defined in the class just shown are called properties. In other languages, they are variously called instance variables, ivars, or data members. You must initialize properties that can store values.
You create instances of simple classes, such as the Processor
class shown in the example in the previous section, by using the class name followed by empty parentheses, as shown here:
let proc = Processor()
After you’ve created an instance, you can access its properties and modify them by using dot syntax:
proc.name = "Z80" proc.dataWidth = 8 print (proc.name)
Properties are values associated with a class or an instance of a class and the methods for accessing them. When they are associated with each instance of a class, they are known as instance properties, and when they are associated with the class itself, they are known as type properties. Properties can be stored values or they can be computed from other properties or values at runtime.
Stored properties are those for which space in memory is allocated to store the property’s value. This is in contrast to computed properties (described a little later) for which no such space is allocated.
You declare stored properties with var
(if they are to be mutable) or let
(if they are to be immutable) inside the class definition. In the Processor()
example from earlier, dataWidth
, addressWidth
, registers
, and name are all examples of stored properties.
Lazy evaluation is the practice of not computing a value until it is actually needed, which is sometimes useful when setting the initial value of a stored property. lazy
property means its initial value won’t be computed until the first time its value is used, as in the following contrived example:
class LazyClass { var aString = "elephant" lazy var bString: String = self.aString + " trunk" }
The following code shows that bString
really is only initialized when its value is referenced, as it uses the modified version of aString
:
var lazyInst = LazyClass() lazyInst.aString // returns "elephant" lazyInst.aString = "tree" lazyInst.bString // returns "tree trunk"
Like computed variables, computed properties do not store a value but are methods that look like properties. They are defined in terms of a getter (identified with the keyword get
, which returns the computed property) and a setter (identified with the keyword set
, which might change the conditions that affect the value returned by the getter). You can also use the getter and setter to read and write other properties, or call other methods of the class. You define a computed property as follows:
class someClass { var propertyName: someType { get { // code that computes and returns // a value of someType } set(valueName) { // code that sets up conditions using valueName } } }
Here is an example of a simple class named Rect
that represents a rectangle in terms of a corner point, a width and a height, and defines a computed property called area to return the area of the Rect
:
class Rect { var x = 0.0, y = 0.0 var width = 0.0, height = 0.0 var area: Double { return (width * height) } }
You could use this as follows:
var q = Rect() q.width = 2.7 q.height = 1.4 q.area // returns 3.78
Property observers are functions you can attach to stored properties and that are called when the value of the property is about to change (identified with the willSet
keyword) or after it has changed (identified with the didSet keyword). The declaration looks as follows:
class Observer { var name: String = "" { willSet(valueName) { // code called before the value is changed } didSet(valueName) { // code called after the value is changed } } }
The willSet
function is called immediately before the property is about to be changed (except for assignment during initialization). The new value is visible inside willSet
as either value Name
, or newValue
if valueName
was not specified.
The didSet
function is called immediately after the property has been changed (except for assignment during initialization).
Static properties. In most cases where you would want to use the (unimplemented) stored type property in a class, you can use a static property instead. To create a static property, precede the property’s definition with the keyword static
, as in the following example:
class Employee { static var nextID = 1 var familyName = "" var givenName = "" var employeeID = 0 }
In this class, only a single instance of the property nextID
exists, regardless of how many employee instances are created. To access a static property use dot-syntax with the class name, as in this example:
var emp = Employee() emp.employeeID = Employee.nextID++
Constant properties. You can declare properties as constants by using the keyword let
. Unlike regular constants, constant properties do not need to be assigned a value when they are defined. Instead, the setting of their value can be deferred until the instance is initialized.
The following example demonstrates a simple class that includes an uninitialized constant property, cp
, the value of which is set by the initializer function:
class ConstPropClass { let cp: Int init(v: Int) { self.cp = v } } var cpDemo = ConstPropClass(v: 8) cpDemo.cp // returns 8
Methods. Methods are functions that are either associated with a class (in which case, they are known as type methods) or with every instance of a class (in which case, they are known as instance methods).
You define methods like functions inside the class definition, as in the following example, which revises the earlier example of the Rect
class to use a method to return the area of the rectangle:
class Rect2 { var x = 0.0, y = 0.0 var width = 0.0, height = 0.0 func area() -> Double { return width * height } }
Type methods. To define a type method for a class, precede the method’s definition with the keyword class
, as in the following example:
class someClass { class func someTypeMethod() { // implementation } }
To call a type method for a class, precede it with the class name using dot syntax:
someClass.someTypeMethod()
Here is a short Swift code example that demonstrates how to implement a custom class in Swift and make it conform to a Comparable
protocol.
import Foundation class Friend : Comparable { let name : String let age : Int init(name : String, age: Int) { self.name = name self.age = age } } func < (lhs: Friend, rhs: Friend) -> Bool { return lhs.age < rhs.age } func > (lhs: Friend, rhs: Friend) -> Bool { return lhs.age > rhs.age } func == (lhs: Friend, rhs: Friend) -> Bool { var returnValue = false if (lhs.name == rhs.name) && (lhs.age == rhs.age) { returnValue = true } return returnValue } let friend1 = Friend(name: "Tom", age: 35) let friend2 = Friend(name: "John", age: 30) print("\Compare Friend object. Same person? (friend1 == friend2)")
Structures
In Swift, structures are closely related to classes. Here are some notable similarities and differences:
• Like classes, structures can have properties, instance and type methods, subscripts, and initializers, and they can support extensions and protocols . • Structures can’t inherit from or be derived from other structures and can’t have deinitializers. • Structures are value types, whereas classes are reference types. This means structures are always copied when assigned or used as arguments to functions; they don’t use reference counts.
The syntax for declaring a new structure is as follows:
struct StructureName { // property, member and related definitions }
Like classes, structures support stored and computed properties, property observers, and constant properties.
Structures also support type properties. While classes introduce these by using the keyword class
, in structures they are introduced with the keyword static
.
Structures can have instance methods, defined by using the same syntax as they are with classes. The following example demonstrates a structure for representing rectangles in terms of a corner point, a width, and a height, and includes a method area()
that computes the area of the shape:
struct Rect1 { var x = 0.0, y = 0.0, width = 0.0, height = 0.0 func area() -> Double { return width * height } }
By default, instance methods defined in a structure are not able to modify that instance’s properties. You can enable this behavior, however, by defining the instance method as a mutating method.
The following example modifies the earlier Rect
structure to include a mutating method embiggenBy()
, which modifies the width
and height
properties:
struct Rect2 { var x = 0.0, y = 0.0, width = 0.0, height = 0.0 mutating func embiggenBy(size: Double) { width += size height += size } }
A mutating method can also replace the current instance of the structure with a new instance by direct assignment to self
.
Enumerations
An enumeration is a user-defined type that consists of a set of named values. With enumerations, algorithms can be more naturally expressed by using the language of the problem set rather than having to manually map values from another type (such as Int
) to some representation of the problem set.
enum TravelClass { case First case Business case Economy }
You can write the same definition more concisely like this:
enum TravelClass { case First, Business, Economy }
Extensions
Swift’s extensions mechanism makes it possible for you to add new functionality to existing classes, structures, and enumerations, even if you did not create them yourself, and you do not have their source code. This is a hugely powerful feature and one that opens opportunities for extending Swift itself - if the language is missing a feature you need, you can often add it yourself as an extension.
The basic syntax of an extension is as follows:
extension Type { // code that extends Type }
Extensions can only add new functionality to existing types; they cannot override existing properties, methods, subscripts, or any other functionality already defined in a type.
Here is an example that extends the String
class to support subscripted character referencing:
extension String { subscript (i: Int) -> Character { return Array(self.characters)[i] } }
To use this, follow a string with a subscript:
"Hello"[4] // returns "o" var a = "Alphabetical" a[0] // returns "A"
Protocols
A protocol defines a standard set of features, expressed via properties, methods, operators, and subscripts, that embody a specific role or provide a specific set of functionality. A protocol isn’t the implementation of this functionality; it just describes what must be implemented.
The syntax for declaring a new protocol is as follows:
protocol ProtocolName { // protocol definition }
The protocol body consists of a series of declarations that define the requirements that must be implemented in order for the adopter to conform to the protocol. This includes a list of the required properties and methods the adopter must define and any constraints on them (e.g., whether a property is readonly or read/write).
protocol Printable { func printable() -> String }
Generics
Swift’s generics feature provides you with the ability to write generic code that can work with any type of data. Parts of the Swift standard library are implemented as generics. For example, the Array
type and the Dictionary
type are generic collections that can store any type of data. In Swift, you can write generic code in a number of ways, including generic functions, generic types, and generic protocols.
To see a classic example of where generics are useful, consider the Swift standard library function swap
, which is defined as follows:
func swap<T>(inout a: T, inout b: T)
From this function definition, you can see swap
takes two inout
parameters, of some type T
, but there is nothing to indicate what type T
actually is. (The two parameters are declared as inout
parameters because the function must swap two existing items, not copies of them).
swap
is a generic function that can swap a pair of Int
s, Double
s, or even a pair of user-defined type instances. It can swap any two variables, as long as they are the same type. To get a better understanding of what swap
is doing, take a look at this next example to consider how it would be implemented:
func swap<T>(inout a: T, inout b: T) { let temp = a a = b b = temp }
Nothing in the body of the function definition is type specific. As long as the constant temp
is the same type as a
(which it will be due to type inferencing), and a is the same type as b
(which it must be according to the types specified in the parameter list of the function), this function can swap any two same-typed values.
You define generic functions by using the following syntax:
func someFunc<Placeholder, [Placeholder...]>(parameterList) { // function body }
The key parts of the definition that indicate this is a generic function are the angle brackets immediately following the function name; these contain one or more type placeholders. These placeholders, called type parameters, stand in for actual types throughout the body of the function.
This next example is a generic struct-based implementation of a queue:
struct Queue<T> { var entries = [T]() mutating func enqueue(item: T) { entries.append(item) } mutating func dequeue() -> T { return entries.removeAtIndex(0) } }
The queued data is stored in an array, entries
, of type T
, and defines two methods: enqueue
(to add an item to the end of the queue) and dequeue
(to pull an item from the beginning of the queue).
So defined, Queue
is now a new, generic type, and you can create queues for integers, strings, and any other data type to which you have access. For example, you can create and use a queue for Int
data as follows:
var q1 = Queue<Int>() q1.enqueue(45) q1.enqueue(39) q1.enqueue(61) q1.enqueue(98) dump(q1) q1.dequeue() dump(q1)
Umbrella Types
Swift provides a few built-in types as general umbrella types, capable of embracing multiple real types under a single heading.
Any. The Any
type is the universal Swift umbrella type. Where an Any
object is expected, absolutely any object or function can be passed, without casting:
func anyExpecter(_ a:Any) {} anyExpecter("howdy") anyExpecter(String.self) anyExpecter(Dog()) anyExpecter(Dog.self) anyExpecter(anyExpecter)
The Any
umbrella type is the general medium of interchange between Swift and the Cocoa Objective-C APIs. When an Objective-C object type is nonspecific (Objective-C id
), it will appear to Swift as Any. Commonly encountered examples are UserDefaults and key–value coding; these allow you to pass an object of indeterminate class along with a string key name, and they allow you to retrieve an object of indeterminate class by a string key name. That object is typed, in Swift, as Any (or as an Optional wrapping Any, so that it can be nil
):
let ud = UserDefaults.standard ud.set(Date(), forKey:"now")
AnyObject. AnyObject
is an empty protocol with the special feature that all class types conform to it automatically. Although Objective-C APIs present Objective-C id
as Any in Swift, Swift AnyObject is Objective-C id
.
A class type can be assigned directly where an AnyObject
is expected; to retrieve it as its original type, you’ll need to cast down:
class Dog {} let d = Dog() let anyo : AnyObject = d let d2 = anyo as! Dog
AnyClass. AnyClass
is the type of AnyObject
. It corresponds to the Objective-C Class
type. It arises typically in declarations where a Cocoa API wants to say that a class is expected.
For example, the UIView
layerClass
class property is declared, in its Swift translation, like this:
class var layerClass : AnyClass {get}
That means: if you override this class property, implement your getter to return a class (which will presumably be a CALayer subclass):
override class var layerClass : AnyClass { CATiledLaye r.self }