- Constraint and auto layout.
- Auto Layout Concepts
- The attributes
leading
andtrailing
are the same asleft
andright
for left-to-right languages such as English, but in a right-to-left environment such as Hebrew or Arabic,leading
andtrailing
are the same asright
andleft
. - Constraints are cumulative, and do not override each other. Setting a second width constraint for a view does not remove or alter the first width constraint.
@IBOutlet weak var prpoerty
.
- ARC
- Function types
- Closure & Type inferring
- SPL = Swift Programming Language book.
-
Swift always chooses
Double
(rather thanFloat
) when inferring the type of floating-point numbers. -
If you combine integer and floating-point literals in an expression, a type of
Double
will be inferred from the context. -
To convert one specific number type to another, you initialize a new number of the desired type with the existing value.
// UInt16(s) -> convert s to UInt16 integer. var ui8: UInt8 = 2_000 var ui16: UInt16 = 1 var combine = UInt16(ui8) + ui16
-
Type Alias
typealias AudioSample = UInt16 var aliasMin = AudioSample.min
-
Swift’s type safety prevents non-Boolean values from being substituted for Bool.
-
Tuples group multiple values into a single compound value.
let HttpError = (404, "HttpError") let (status, description) = HttpError println("Status Code \(Status)") println("Description is \(Description)")
-
You use Optionals in situations where a value may be absent. There exist a value or could be
nil
when its optional. -
nil
cannot be used with nonoptional constants and variables. If the value of a variable or constant may be absent, then should declare it as optional type. -
An optional Int is written as
Int?
, notInt
. The question mark indicates that the value it contains is optional, meaning that it might contain someInt
value, or it might contain no value at all.// Declare optional type without default value, then it is automatically set to nil. var surveyAnswer: String?
-
Optional binding can be used with
if
andwhile
statements to check for a value inside an optional.if let constantName = someOptional, varName = anotherOptional { statements } if let actualNumber = possibleNumber.toInt() { println("\'\(possibleNumber)\' has an integer value of \(actualNumber)") } else { println("\'\(possibleNumber)\' could not be converted to an integer") }
-
Implicitly Unwrap optionals Sometimes it is clear from a program’s structure that an optional will always have a value, after that value is first set. It is useful to remove the need to check and unwrap the optional’s value every time it is accessed, because it can be safely assumed to have a value all of the time.
let possibleString: String? = "An optional string." let forcedString: String = possibleString! // requires an exclamation mark let assumedString: String! = "An implicitly unwrapped optional string." let implicitString: String = assumedString // no need for an exclamation mark
-
You can also use an implicitly unwrapped optional with optional binding, to check and unwrap its value in a single statement.
let assumedString: String! = "An implicitly unwrapped optional string." if let definiteString = assumedString { println(definiteString) }
-
Use an assertion whenever a condition has the potential to be false, but must definitely be true in order for your code to continue execution.
let age = -3 assert(age >= 0, "A person's age cannot be less than zero") // this causes the assertion to trigger, because age is not >= 0
###Reading - SPL: Basic Operators
-
Prefix
++
or postfix++
.var a = 0 let b = ++a // a and b are now both equal to 1 let c = a++ // a is now equal to 2, but c has been set to the pre-increment value of 1
-
Minus and Plus operator.
let three = 3 let minusThree = -three // minusThree equals -3 let plusThree = +minusThree // Thoguh have plus operator, minusThree equals -3
-
The compound assignment operators(
+=
) do not return a value. You cannot writelet b = a += 2
-
The nil coalescing operator (
a ?? b
) unwraps an optionala
if it contains a value, or returns a default valueb
ifa
is nil. The expression a is always of an optional type.(a ?? b) == (a != nil ? a! : b)
-
Range operators
(a...b) // include a to b, not the same as Ruby's syntax (a..<b) // include a but not b. Half-open range operators.
###Reading - SPL: Strings and Characters
-
Create an empty String value as the starting point for building a longer string by assigning to
""
orString()
.var emptyString = "" // empty string literal var anotherEmptyString = String() // initializer syntax // these two strings are both empty, and are equivalent to each other
-
String Mutability
var variableString = "Horse" variableString += " and carriage" // variableString is now "Horse and carriage" let constantString = "Highlander" constantString += " and another Highlander" // this reports a compile-time error - a constant string cannot be modified
-
String are Value types. String variable is passing to an method's argument by copying by value.
-
Claim a Character type by explicitly
Character
annotation.let exclamationMark: Character = "!" let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"] let catString = String(catCharacters) println(catString) // prints "Cat!🐱"
-
You can append a Character value to a String variable with the String type’s append() method.
let exclamationMark: Character = "!" welcome.append(exclamationMark) // welcome now equals "hello there!"
-
Special Characters in String Literals
let wiseWords = "\"Imagination is more" + " important than knowledge\" - Einstein" // "Imagination is more important than knowledge" - Einstein let dollarSign = "\u{24}" // $, Unicode scalar U+0024 let blackHeart = "\u{2665}" // ♥, Unicode scalar U+2665 let sparklingHeart = "\u{1F496}" // 💖, Unicode scalar U+1F496
-
Extended Grapheme Clusters: Every instance of Swift’s
Character
type represents a single extended grapheme cluster. An extended grapheme cluster is a sequence of one or more Unicode scalars that (when combined) produce a single human-readable character.let eAcute: Character = "\u{E9}" // é let combinedEAcute: Character = "\u{65}\u{301}" // e followed by ́ // eAcute is é, combinedEAcute is é
-
Counting Characters: if you initialize a new string with the four-character word cafe, and then append a
COMBINING ACUTE ACCENT (U+0301)
to the end of the string, the resulting string will still have a character count of 4, with a fourth character of é, not e.let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪" println("unusualMenagerie has \(count(unusualMenagerie)) characters") // prints "unusualMenagerie has 40 characters"
-
Use the
startIndex
property to access the position of the firstCharacter
of aString
, and theendIndex
property to access the posision of the last. If the String is empty, startIndex and endIndex are equal.let greeting = "Guten Tag" println(greeting.startIndex) // 0 println(greeting.endIndex) // 9 // Use the global function indicies(_:) to create a Range // of all of the indexes used to access individual characters in a string. for (index in indices(greeting)) { print("\(greeting[index]) ") } println("\n") // prints "G u t e n T a g"
-
insert(_:atIndex:)
,removeAtIndex(_:)
,splice(_:atIndex:)
, andremoveRange(_:)
.var welcome = "hello" welcome.insert("!", atIndex: welcome.endIndex) // To insert another string at a specified index, use splice welcome.splice(" there", atIndex: welcome.endIndex.predecessor()) // welcome now equals "hello there!" // To remove a character from a string at a specified index, use removeAtIndex welcome.removeAtIndex(welcome.endIndex.predecessor()) // welcome now equals "hello there" //To remove a substring at a specified range, use the removeRange let range = advance(welcome.endIndex, -6)..<welcome.endIndex welcome.removeRange(range) // welcome now equals "hello"
-
Two String values (or two Character values) are considered equal if their extended grapheme clusters are canonically equivalent.
// "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE let eAcuteQuestion = "Voulez-vous un caf\u{E9}?" // "Voulez-vous un café?" using // LATIN SMALL LETTER E and COMBINING ACUTE ACCENT let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?" if eAcuteQuestion == combinedEAcuteQuestion { println("These two strings are considered equal") } // prints "These two strings are considered equal"
-
hasPrefix(_)
,hasSuffix(_)
accept a String argument. ThehasPrefix(_:)
andhasSuffix(_:)
methods perform a character-by-character canonical equivalence comparison between the extended grapheme clusters in each string. -
utf8
orutf16
.for codeUnit in dogString.utf8 { print("\(codeUnit) ") } print("\n") // 68 111 103 226 128 188 240 159 144 182
-
You can access a Unicode scalar representation of a String value by iterating over its unicodeScalars property. This property is of type
UnicodeScalarView
, which is a collection of values of typeUnicodeScalar
. -
Each
UnicodeScalar
has a value property that returns the scalar’s 21-bit value, represented within a UInt32 value.for scalar in dogString.unicodeScalars { print("\(scalar.value) ") } print("\n") // 68 111 103 8252 128054 // 8252 to hex decimal is 203c // which is U+203c for the DOUBLE EXCLAMATION MARK character for scalar in dogString.unicodeScalars { print("\(scalar) ") } // D o g ‼ 🐶
###Reading - SPL: Collection Types
-
Arrays
var someInts = [Int]() someInts.append(3) // someInts now contains 1 value of type Int someInts = [] // someInts is now an empty array, but is still of type [Int] // another initializer of array var threeDoubles = [Double](count: 3, repeatedValue: 0.0) // threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0] // Array Literal var ary: [Int] = [2,3,4,5] // declare by Swift’s type inference var arys = [2,3,4,5,5]
-
You can also use subscript syntax to change a range of values at once, even if the replacement set of values has a different length than the range you are replacing.
var shoppingList = ["s", "h", "o", "p", "p", "i", "n", "g", "L"] shoppingList[4...6] = ["Bananas", "Apples"] // shoppingList now contains 6 items
-
You can’t use subscript syntax to append a new item to the end of an array. But can use additional assignment operator.
shoppingList += ["Baking Powder"] // shoppingList now contains 4 items shoppingList += ["Chocolate Spread", "Cheese", "Butter"] // shoppingList now contains 7 items var firstItem = shoppingList[0] // use subscript syntax to retrieve firstItem is equal to "Eggs"
-
use
insert(_:atIndex:)
method to insert value to specific index.shoppingList.insert("Maple Syrup", atIndex: 0)
-
A set stores distinct values of the same type in a collection with no defined ordering. The type of a Swift set is written as
Set<SomeType>
, whereSomeType
is the type that the set is allowed to store. Unlike arrays, sets do not have an equivalent shorthand form.var letters = Set<Character>() println("letters is of type Set<Character> with \(letters.count) items.") // prints "letters is of type Set<Character> with 0 items." letters.insert("a") // letters now contains 1 value of type Character letters = [] // letters is now an empty set, but is still of type Set<Character>
-
Set use
inset(_:)
,remove(_:)
,contains(_:)
method to add, remove, check elements. -
Iterate set cannot gurantee its iteration order, but can pass set to
sorted
method then iterate out.for genre in sorted(favoriteGenres) { println("\(genre)") } // Classical // Hip hop // Jazz
-
Use
union(_:)
,subtract(_:)
,intersect(_:)
, andexclusiveOr(_:)
to operate set relations. -
Use
isSubsetOf(_:)
,==
,isSupersetOf(_:)
,isStrictSubsetOf(_:)
, orisStrictSupersetOf(_:)
, andisDisjointWith(_:)
to compare sets. -
A type must be hashable in order to be stored in a set—that is, the type must provide a way to compute a hash value for itself. A hash value is an Int value that is the same for all objects that compare equal, such that if
a == b
, it follows thata.hashValue == b.hashValue
. -
Dictionary
var namesOfIntegers = [Int: String]() // namesOfIntegers is an empty [Int: String] dictionary namesOfIntegers[16] = "sixteen" // namesOfIntegers now contains 1 key-value pair namesOfIntegers = [:] // namesOfIntegers is once again an empty dictionary of type [Int: String] // Dictionary literals var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
-
As an alternative to subscripting, use a dictionary’s
updateValue(_:forKey:)
method to set or update the value for a particular key. -
The
updateValue(_:forKey:)
method returns an optional value of the dictionary’s value type. This optional value contains the old value for that key if one existed before the update, ornil
if no value existed.var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"] if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") { println("The old value for DUB was \(oldValue).") } // prints "The old value for DUB was Dublin."
-
You can also use subscript syntax to retrieve a value from the dictionary for a particular key. If the key does not exist, then return
nil
. Also, we can remove a key-value pair by assignnil
value to a key, orremoveValueForKey(_:)
.airports["APL"] = "Apple International" // "Apple International" is not the real airport for APL, // so delete it airports["APL"] = nil // APL has now been removed from the dictionary
-
Each item in the dictionary is returned as a
(key, value)
tuple. -
Swift’s
Dictionary
type does not have a defined ordering. To iterate over the keys or values of a dictionary in a specific order, use the globalsorted
function on its keys or values property.
for airportCode in sorted(airports.keys) {
println("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR
###Reading - SPL: Control Flow
-
In contrast with switch statements in C and Objective-C,
switch
statements in Swift do not fall through the bottom of each case and into the next one by default. Instead, the entireswitch
statement finishes its execution as soon as the first matching switch case is completed, without requiring an explicitbreak
statement. -
A
switch
case can bind the value or values it matches to temporary constants or variables, for use in the body of the case. This is known as value binding.let anotherPoint = (2, 0) switch anotherPoint { case (let x, 0): println("on the x-axis with an x value of \(x)") case (0, let y): println("on the y-axis with a y value of \(y)") case let (x, y): println("somewhere else at (\(x), \(y))") } // prints "on the x-axis with an x value of 2"
-
Use
where
to check for additional conditions.let yetAnotherPoint = (1, -1) switch yetAnotherPoint { case let (x, y) where x == y: println("(\(x), \(y)) is on the line x == y") case let (x, y) where x == -y: println("(\(x), \(y)) is on the line x == -y") case let (x, y): println("(\(x), \(y)) is just some arbitrary point") } // prints "(1, -1) is on the line x == -y"
-
Control transfer statements:
continue
,break
,fallthrough
, andreturn
. -
If you really need C-style fallthrough behavior, you can opt in to this behavior on a case-by-case basis with the fallthrough keyword. The example below uses fallthrough to create a textual description of a number:
let integerToDescribe = 5 var description = "The number \(integerToDescribe) is" switch integerToDescribe { case 2, 3, 5, 7, 11, 13, 17, 19: description += " a prime number, and also" fallthrough default: description += " an integer." } println(description) // prints "The number 5 is a prime number, and also an integer."
-
Label statements, use label statments combine with control transfer statments to control the flow.
gameLoop: while square != finalSquare { if ++diceRoll == 7 { diceRoll = 1 } switch square + diceRoll { case finalSquare: // diceRoll will move us to the final square, so the game is over break gameLoop case let newSquare where newSquare > finalSquare: // diceRoll will move us beyond the final square, so roll again continue gameLoop default: // this is a valid move, so find out its effect square += diceRoll square += board[square] } }
-
The return value of a function can be ignored when a function without return value is called:
func printAndCount(stringToPrint: String) -> Int { println(stringToPrint) return count(stringToPrint) } func printWithoutCounting(stringToPrint: String) { printAndCount(stringToPrint) } printAndCount("hello, world") // prints "hello, world" and returns a value of 12 printWithoutCounting("hello, world") // prints "hello, world" but does not return a value
-
Function with multiple return values
func minMax(array: [Int]) -> (min: Int, max: Int) { var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) } let bounds = minMax([8, -6, 2, 109, 3, 71]) println("min is \(bounds.min) and max is \(bounds.max)") // prints "min is -6 and max is 109"
-
Optional return types
func minMax(array: [Int]) -> (min: Int, max: Int)? { if array.isEmpty { return nil } var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) } // use optional binding to check whether this // version of the minMax function returns an actual tuple value or nil if let bounds = minMax([8, -6, 2, 109, 3, 71]) { println("min is \(bounds.min) and max is \(bounds.max)") } // prints "min is -6 and max is 109"
-
Sometimes it’s useful to name each parameter when you call a function, to indicate the purpose of each argument you pass to the function.
// External Parameter Names func join(string s1: String, toString s2: String, withJoiner joiner: String) -> String { return s1 + joiner + s2 } join(string: "hello", toString: "world", withJoiner: ", ") // returns "hello, world"
-
Shorthand External Parameter Names with
#
.func containsCharacter(#string: String, #characterToFind: Character) -> Bool { for character in string { if character == characterToFind { return true } } return false } let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v") // containsAVee equals true, because "aardvark" contains a "v"
-
Default Parameter Values.
func join(string s1: String, toString s2: String, withJoiner joiner: String = "-") -> String { return s1 + joiner + s2 } join(string: "hello", toString: "world") // returns "hello-world
-
Variadic Parameters with(
...
).func arithmeticMean(numbers: Double...) -> Double { var total: Double = 0 for number in numbers { total += number } return total / Double(numbers.count) } arithmeticMean(1, 2, 3, 4, 5) // returns 3.0, which is the arithmetic mean of these five numbers arithmeticMean(3, 8.25, 18.75) // returns 10.0, which is the arithmetic mean of these three numbers
-
Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. Define variable parameters by prefixing the parameter name with the keyword
var
.func alignRight(var string: String, totalLength: Int, pad: Character) -> String { let amountToPad = totalLength - count(string) if amountToPad < 1 { return string } let padString = String(pad) for _ in 1...amountToPad { string = padString + string } return string } let originalString = "hello" let paddedString = alignRight(originalString, 10, "-") // paddedString is equal to "-----hello" // originalString is still equal to "hello"
-
Variable parameters, as described above, can only be changed within the function itself. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
-
You can only pass a variable as the argument for an in-out parameter. You cannot pass a constant or a literal value as the argument, because constants and literals cannot be modified. You place an ampersand (&) directly before a variable’s name when you pass it as an argument to an inout parameter.
func swapTwoInts(inout a: Int, inout b: Int) { let temporaryA = a a = b b = temporaryA } var someInt = 3 var anotherInt = 107 swapTwoInts(&someInt, &anotherInt) println("someInt is now \(someInt), and anotherInt is now \(anotherInt)") // prints "someInt is now 107, and anotherInt is now 3"
-
Function type.
func printHelloWorld() { println("hello, world") } // Function type: () -> () var mathFunction: (Int, Int) -> Int = addTwoInts // function type as parameter type func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) { println("Result: \(mathFunction(a, b))") } printMathResult(addTwoInts, 3, 5) // prints "Result: 8" // function type as return type func chooseStepFunction(backwards: Bool) -> (Int) -> Int { return backwards ? stepBackward : stepForward }
-
Nested functions.
func chooseStepFunction(backwards: Bool) -> (Int) -> Int { // two nested functions. func stepForward(input: Int) -> Int { return input + 1 } func stepBackward(input: Int) -> Int { return input - 1 } return backwards ? stepBackward : stepForward } var currentValue = -4 let moveNearerToZero = chooseStepFunction(currentValue > 0) // moveNearerToZero now refers to the nested stepForward() function while currentValue != 0 { println("\(currentValue)... ") currentValue = moveNearerToZero(currentValue) } println("zero!") // -4... // -3... // -2... // -1... // zero!
-
Enumeration.
enum CompassPoint { case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } var directionToHead = CompassPoint.West // you can set it to a different CompassPoint value using a shorter dot syntax directionToHead = .East
-
Matching Enumeration Values with a Switch Statement.
directionToHead = .South switch directionToHead { case .North: println("Lots of planets have a north") case .South: println("Watch out for penguins") case .East: println("Where the sun rises") case .West: println("Where the skies are blue") } // prints "Watch out for penguins"
-
Associated Values.
// This can be read as: // “Define an enumeration type called Barcode, // which can take either a value of // UPCA with an associated value of type (Int, Int, Int, Int), // or a value of QRCode with an associated value of type String.” enum Barcode { case UPCA(Int, Int, Int, Int) case QRCode(String) }
-
Constants and variables of type Barcode can store either a
.UPCA
or a.QRCode
(together with their associated values), but they can only store one of them at any given time.switch productBarcode { case .UPCA(let numberSystem, let manufacturer, let product, let check): println("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).") case .QRCode(let productCode): println("QR code: \(productCode).") } // prints "QR code: ABCDEFGHIJKLMNOP." // If all of the associated values // for an enumeration member are extracted as constants, // or if all are extracted as variables, // you can place a single var or let annotation before the member name. switch productBarcode { case let .UPCA(numberSystem, manufacturer, product, check): println("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).") case let .QRCode(productCode): println("QR code: \(productCode).") } // prints "QR code: ABCDEFGHIJKLMNOP."
-
The barcode example in Associated Values shows how members of an enumeration can declare that they store associated values of different types. As an alternative to associated values, enumeration members can come prepopulated with default values (called raw values), which are all of the same type.
enum ASCIIControlCharacter: Character { case Tab = "\t" case LineFeed = "\n" case CarriageReturn = "\r" }
-
Note that raw values are not the same as associated values. Raw values are set to prepopulated values when you first define the enumeration in your code.
-
The raw value for a particular enumeration member is always the same. Associated values are set when you create a new constant or variable based on one of the enumeration’s members, and can be different each time you do so.
-
Raw values can be
strings
,characters
, orany of the integer or floating-point number types
. Each raw value must be unique within its enumeration declaration. When integers are used for raw values, they auto-increment if no value is specified for some of the enumeration members.enum Planet: Int { case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } let earthsOrder = Planet.Earth.rawValue // earthsOrder is 3
-
Initializing from a Raw Value. If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value’s type (as a parameter called rawValue) and returns either an enumeration member or
nil
.let possiblePlanet = Planet(rawValue: 7) // possiblePlanet is of type Planet? and equals Planet.Uranus
-
Not all possible Int values will find a matching planet, however. Because of this, the raw value initializer always returns an optional enumeration member. In the example above, possiblePlanet is of type
Planet?
, or “optional Planet.” -
The raw value initializer is a failable initializer, because not every raw value will return an enumeration member.
-
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.
-
Closure syntax.
{ (parameters) -> return type in statements }
-
Because a closure is passed as an argument to a function, Swift can infer the types of its parameters and the type of the value it returns from the type of the function’s parameter.
reversed = sorted(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } ) // inferring types reversed = sorted(names, { s1, s2 in return s1 > s2 } )
-
Advancely, Single-expression closures can implicitly return the result of their single expression by omitting the return keyword from their declaration.
reversed = sorted(names, { s1, s2 in s1 > s2 } )
-
Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names
$0
,$1
,$2
, and so on. -
If you use these shorthand argument names within your closure expression, you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type. The
in
keyword can also be omitted, because the closure expression is made up entirely of its body.reversed = sorted(names, { $0 > $1 } )
-
There’s actually an even shorter way to write the closure expression above. Swift’s
String
type defines its string-specific implementation of the greater-than operator (>
) as a function that has two parameters of type String, and returns a value of typeBool
.reversed = sorted(names, >)
-
Trailing Closures. If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead.
func someFunctionThatTakesAClosure(closure: () -> ()) { // function body goes here } // here's how you call this function without using a trailing closure: someFunctionThatTakesAClosure({ // closure's body goes here }) // here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() { // trailing closure's body goes here }
reversed = sorted(names) { $0 > $1 }
```swift
let strings = numbers.map {
(var number) -> String in
var output = ""
while number > 0 {
output = digitNames[number % 10]! + output
number /= 10
}
return output
}
// strings is inferred to be of type [String]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]
-
A closure can capture constants and variables from the surrounding context in which it is defined.
-
In Swift, the simplest form of a closure that can capture values is a nested function, written within the body of another function. A nested function can capture any of its outer function’s arguments and can also capture any constants and variables defined within the outer function.
func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer } let incrementByTen = makeIncrementer(forIncrement: 10) incrementByTen() // returns a value of 10 incrementByTen() // returns a value of 20 incrementByTen() // returns a value of 30 // If you create a second incrementer, // it will have its own stored reference to a new, separate runningTotal variable. let incrementBySeven = makeIncrementer(forIncrement: 7) incrementBySeven() // returns a value of 7 // Calling the original incrementer (incrementByTen) again continues to // increment its own runningTotal variable, // and does not affect the variable captured by incrementBySeven. incrementByTen() // returns a value of 40
-
Closures are feference types. Whenever you assign a function or a closure to a constant or a variable, you are actually setting that constant or variable to be a reference to the function or closure. This also means that if you assign a closure to two different constants or variables, both of those constants or variables will refer to the same closure.
// incrementByTen now returns a value of 40 let alsoIncrementByTen = incrementByTen alsoIncrementByTen() // returns a value of 50
###Reading - SPL: Classes and Structures
-
Classes have additional capabilities that structures do not:
- Inheritance enables one class to inherit the characteristics of another.
- Type casting enables you to check and interpret the type of a class instance at runtime.
- Deinitializers enable an instance of a class to free up any resources it has assigned.
- Reference counting allows more than one reference to a class instance.
-
Definition of Syntax.
struct Resolution { var width = 0 var height = 0 } class VideoMode { var resolution = Resolution() var interlaced = false var frameRate = 0.0 var name: String? } // create instance by initializer let someResolution = Resolution() let someVideoMode = VideoMode() // Accessing properties someVideoMode.resolution.width = 1280 println("The width of someVideoMode is now \(someVideoMode.resolution.width)") // prints "The width of someVideoMode is now 1280"
-
All structures have an automatically-generated memberwise initializer. which you can use to initialize the member properties of new structure instances. This means that any structure and enumeration instances you create —and any value types they have as properties— are always copied when they are passed around in your code. Unlike structures, class instances do not receive a default memberwise initializer.
let vga = Resolution(width: 640, height: 480)
-
A value type is a type whose value is copied when it is assigned to a variable or constant, or when it is passed to a function. All structures and enumerations are value types in Swift.
let hd = Resolution(width: 1920, height: 1080) var cinema = hd cinema.width = 2048 println("cinema is now \(cinema.width) pixels wide") // prints "cinema is now 2048 pixels wide" println("hd is still \(hd.width) pixels wide") // prints "hd is still 1920 pixels wide"
// The same behavior applies to enumerations enum CompassPoint { case North, South, East, West } var currentDirection = CompassPoint.West let rememberedDirection = currentDirection currentDirection = .East if rememberedDirection == .West { println("The remembered direction is still .West") } // prints "The remembered direction is still .West"
-
Classes are reference types.
-
To identity two constants or variables are refer to the same instance of class, use identity operator
===
or!==
.if tenEighty === alsoTenEighty { println("tenEighty and alsoTenEighty refer to the same VideoMode instance.") } // prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
-
Unlike Objective-C, There has no explicit pointers in Swift.
-
Swift’s String, Array, and Dictionary types are implemented as structures. This means that strings, arrays, and dictionaries are copied when they are assigned to a new constant or variable, or when they are passed to a function or method.
-
This behavior is different from
NSString
,NSArray
, andNSDictionary
inFoundation
, which are implemented as classes, not structures. -
Choosing Between Classes and Structures. Consider creating a structure when one or more of these conditions apply:
- The structure’s primary purpose is to encapsulate a few relatively simple data values.
- It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an instance of that structure.
- Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
- The structure does not need to inherit properties or behavior from another existing type.
-
If you create an instance of a structure and assign that instance to a constant, you cannot modify the instance’s properties, even if they were declared as variable properties.
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) // this range represents integer values 0, 1, 2, and 3 rangeOfFourItems.firstValue = 6 // this will report an error, even though firstValue is a variable property
-
A stored property is a constant or variable that is stored as part of an instance of a particular class or structure. It could be either variable stored properties (introduced by the
var
keyword) or constant stored properties (introduced by thelet
keyword). -
A azy stored property is a property whose initial value is not calculated until the first time it is used.
class DataManager { lazy var importer = DataImporter() var data = [String]() } let manager = DataManager() manager.data.append("Some data") manager.data.append("Some more data") // the DataImporter instance for // the importer property has not yet been created. println(manager.importer.fileName) // the DataImporter instance for // the importer property has now been created // prints "data.txt"
-
Classes, structures, and enumerations can define computed properties, which do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.
struct Rect { var origin = Point() var size = Size() var center: Point { get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: centerY) } set(newCenter) { origin.x = newCenter.x - (size.width / 2) origin.y = newCenter.y - (size.height / 2) } } } var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0)) let initialSquareCenter = square.center square.center = Point(x: 15.0, y: 15.0) println("square.origin is now at (\(square.origin.x), \(square.origin.y))") // prints "square.origin is now at (10.0, 10.0)"
-
Shorthand Setter Declaration. If a computed property’s setter does not define a name for the new value to be set, a default name of
newValue
is used.struct AlternativeRect { var origin = Point() var size = Size() var center: Point { get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: centerY) } set { origin.x = newValue.x - (size.width / 2) origin.y = newValue.y - (size.height / 2) } } }
-
A computed property with a getter but no setter is known as a read-only computed property. A read-only computed property always returns a value, and can be accessed through dot syntax, but cannot be set to a different value.
-
You can simplify the declaration of a read-only computed property by removing the
get
keyword and its braces.struct Cuboid { var width = 0.0, height = 0.0, depth = 0.0 var volume: Double { return width * height * depth } }
-
Property observers observe and respond to changes in a property’s value. Property observers are called every time a property’s value is set, even if the new value is the same as the property’s current value.
willSet
is called just before the value is stored.didSet
is called immediately after the new value is stored.
-
If you implement a willSet observer, it is passed the new property value as a constant parameter. You can specify a name for this parameter as part of your
willSet
implementation. If no to do so, the parameter will still be made available with a default parameter name ofnewValue
. -
Similarly, you can name the parameter if you wish, or use the default parameter name of
oldValue
when you implementdidSet
observer.class StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { println("About to set totalSteps to \(newTotalSteps)") } didSet { if totalSteps > oldValue { println("Added \(totalSteps - oldValue) steps") } } } } let stepCounter = StepCounter() stepCounter.totalSteps = 200 // About to set totalSteps to 200 // Added 200 steps stepCounter.totalSteps = 360 // About to set totalSteps to 360 // Added 160 steps stepCounter.totalSteps = 896 // About to set totalSteps to 896 // Added 536 steps
-
You can also define properties that belong to the type itself, not to any one instance of that type. There will only ever be one copy of these properties, no matter how many instances of that type you create. These kinds of properties are called type properties.
-
For value types (that is, structures and enumerations), you can define stored and computed type properties. For classes, you can define computed type properties only.
-
Stored type properties for value types can be variables or constants. Computed type properties are always declared as variable properties, in the same way as computed instance properties.
-
You define type properties with the static keyword. For computed type properties for class types, you can use the class keyword instead to allow subclasses to override the superclass’s implementation.
struct SomeStructure { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { // return an Int value here } } enum SomeEnumeration { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { // return an Int value here } } class SomeClass { static var storedTypeProperty = "Some value." static var computedTypeProperty: Int { // return an Int value here } class var overrideableComputedTypeProperty: Int { // return an Int value here } }
-
Type properties are queried and set with dot syntax, just like instance properties. However, type properties are queried and set on the type, not on an instance of that type.
struct AudioChannel { static let thresholdLevel = 10 static var maxInputLevelForAllChannels = 0 var currentLevel: Int = 0 { didSet { if currentLevel > AudioChannel.thresholdLevel { // cap the new audio level to the threshold level currentLevel = AudioChannel.thresholdLevel } if currentLevel > AudioChannel.maxInputLevelForAllChannels { // store this as the new overall maximum input level AudioChannel.maxInputLevelForAllChannels = currentLevel } } } } var leftChannel = AudioChannel() var rightChannel = AudioChannel() leftChannel.currentLevel = 7 println(leftChannel.currentLevel) // prints "7" println(AudioChannel.maxInputLevelForAllChannels) // prints "7" rightChannel.currentLevel = 11 println(rightChannel.currentLevel) // prints "10" println(AudioChannel.maxInputLevelForAllChannels) // prints "10"
-
Talk about protocol in
Enum
, computed properties, optional binding, recursion, Model, passing tail closure to block shorthand. -
Each enumeration definition defines a brand new type. Like other types in Swift, their names should start with a capital letter.
-
Dictionary always return
optional
type, a key may not exist.
-
Optional
just another version ofEnum
.var x: String? = nil println(x === Optional<String>.None) // true enum Optional<T>{ case None case Some(T) } enum Optional<String>{ case None case Some(String) } x = "Hello" println(x == Optional<String>.Some("Hello")) // true
-
var x = y!
is the same as:var y: String? = "Hello" var x = y! // is equivalent to enum Optional<String>{ case Some(let value): y = value case None: // raise an exception. }
-
NSObject
,NSNumber
,NSDate
,NSData
. -
override
andfinal
keyword for methods. -
Only
var
canlazy
initialize. -
If a
struct
has no initializer, it will have default one with all properties as arguments.
-
Methods are functions that are associated with a particular type. Classes, structures, and enumerations can all define instance methods.
-
Swift gives the first parameter name in a method a local parameter name by default, and gives the second and subsequent parameter names both local and external parameter names by default.
-
You don’t need to define an external parameter name for the first argument value, because its purpose is clear from the function name.
let counter = Counter() counter.incrementBy(5, numberOfTimes: 3) // counter value is now 15
-
If you do not want to provide an external name for the second or subsequent parameter of a method, override the default behavior by using an underscore character (
_
) as an explicit external parameter name for that parameter. -
Every instance of a type has an implicit property called
self
, which is exactly equivalent to the instance itself.struct Point { var x = 0.0, y = 0.0 func isToTheRightOfX(x: Double) -> Bool { return self.x > x } } let somePoint = Point(x: 4.0, y: 5.0) if somePoint.isToTheRightOfX(1.0) { println("This point is to the right of the line where x == 1.0") } // prints "This point is to the right of the line where x == 1.0" // Without the self prefix, Swift would assume that // both uses of x referred to the method parameter called x.
-
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
-
The method can also assign a completely new instance to its implicit
self
property, and this new instance will replace the existing one when the method ends.struct Point { var x = 0.0, y = 0.0 mutating func moveByX(deltaX: Double, y deltaY: Double) { x += deltaX y += deltaY } } var somePoint = Point(x: 1.0, y: 1.0) somePoint.moveByX(2.0, y: 3.0) println("The point is now at (\(somePoint.x), \(somePoint.y))") // prints "The point is now at (3.0, 4.0)"
-
You cannot call a mutating method on a constant of structure type, because its properties cannot be changed, even if they are variable properties.
let fixedPoint = Point(x: 3.0, y: 3.0) fixedPoint.moveByX(2.0, y: 3.0) // this will report an error
-
Mutating methods can assign an entirely new instance to the implicit
self
property. -
You indicate type methods by writing the keyword
static
before the method’s func keyword. Classes may also use theclass
keyword to allow subclasses to override the superclass’s implementation of that method. -
Within the body of a type method, the implicit
self
property refers to the type itself, rather than an instance of that type. -
A type method can call another type method with the other method’s name, without needing to prefix it with the type name.
-
Similarly, type methods on structures and enumerations can access type properties by using the type property’s name without a type name prefix.
struct LevelTracker { static var highestUnlockedLevel = 1 static func unlockLevel(level: Int) { if level > highestUnlockedLevel { highestUnlockedLevel = level } } static func levelIsUnlocked(level: Int) -> Bool { return level <= highestUnlockedLevel } var currentLevel = 1 mutating func advanceToLevel(level: Int) -> Bool { if LevelTracker.levelIsUnlocked(level) { currentLevel = level return true } else { return false } } }
-
Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence.
-
Unlike instance methods, subscripts can be read-write or read-only. This behavior is communicated by a getter and setter in the same way as for computed properties.
subscript(index: Int) -> Int { get { // return an appropriate subscript value here } set(newValue) { // perform a suitable setting action here } }
-
Subscript usage:
struct Matrix { let rows: Int, columns: Int var grid: [Double] init(rows: Int, columns: Int) { self.rows = rows self.columns = columns grid = Array(count: rows * columns, repeatedValue: 0.0) } func indexIsValidForRow(row: Int, column: Int) -> Bool { return row >= 0 && row < rows && column >= 0 && column < columns } subscript(row: Int, column: Int) -> Double { get { assert(indexIsValidForRow(row, column: column), "Index out of range") return grid[(row * columns) + column] } set { assert(indexIsValidForRow(row, column: column), "Index out of range") grid[(row * columns) + column] = newValue } } } var matrix = Matrix(rows: 2, columns: 2) matrix[0, 1] = 1.5 matrix[1, 0] = 3.2
-
A subclass can provide its own custom implementation of an instance method, type method, instance property, type property, or subscript that it would otherwise inherit from a superclass. This is known as overriding.
-
An overridden method named
someMethod()
can call the superclass version ofsomeMethod()
by callingsuper.someMethod()
within the overriding method implementation. -
An overridden subscript for
someIndex
can access the superclass version of the same subscript assuper[someIndex]
from within the overriding subscript implementation. -
You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override. You cannot, however, present an inherited read-write property as a read-only property.
-
You can use property overriding to add property observers to an inherited property. This enables you to be notified when the value of an inherited property changes, regardless of how that property was originally implemented.
-
You can prevent a method, property, or subscript from being overridden by marking it as
final
. -
You can mark an entire class as final by writing the final modifier before the class keyword in its class definition (
final class
). Any attempt to subclass a final class is reported as a compile-time error.
###Reading - SPL: Initialization
-
If a property always takes the same initial value, provide a default value rather than setting a value within an initializer.
// Setting Initial Values for Stored Properties struct Fahrenheit { var temperature: Double init() { temperature = 32.0 } } // Default Property Values struct Fahrenheit { var temperature = 32.0 }
-
Initializers do not have an identifying function name before their parentheses in the way that functions and methods do. Therefore, the names and types of an initializer’s parameters play a particularly important role in identifying which initializer should be called.
-
Swift provides an automatic external name for every parameter in an initializer if you don’t provide an external name yourself. This automatic external name is the same as the local name.
-
If you do not want to use an external name for an initializer parameter, write an underscore (
_
) instead of an explicit external name for that parameter to override the default behavior.struct Color { let red, green, blue: Double init(red: Double, green: Double, blue: Double) { self.red = red self.green = green self.blue = blue } init(white: Double) { red = white green = white blue = white } init(_ black: Double){ } } let magenta = Color(red: 1.0, green: 0.0, blue: 1.0) let halfGray = Color(white: 0.5) let veryGreen = Color(0.0, 1.0, 0.0) // this reports a compile-time error - external names are required
-
Properties of optional type are automatically initialized with a value of
nil
, indicating that the property is deliberately intended to have “no value yet” during initialization. -
Once a constant property is assigned a value, it can’t be further modified. For class instances, a constant property can only be modified during initialization by the class that introduces it. It cannot be modified by a subclass.
-
The default initializer simply creates a new instance with all of its properties set to their default values. The instance property must be provided default values, otherwise will raise compiler-error.
class ShoppingListItem { var name: String? var quantity = 1 var purchased = false // let y: Int // will raise compile-error } // Default initializer var item = ShoppingListItem()
-
Unlike a default initializer, the structure receives a memberwise initializer even if it has stored properties that do not have default values.
struct Size { var width = 0.0, height = 0.0 } let twoByTwo = Size(width: 2.0, height: 2.0)
-
Initializers can call other initializers to perform part of an instance’s initialization by
self.init()
. This process, known as initializer delegation, avoids duplicating code across multiple initializers. -
You can only call
self.init
from within an initializer.struct Rect { var origin = Point() var size = Size() init() {} init(origin: Point, size: Size) { self.origin = origin self.size = size } init(center: Point, size: Size) { let originX = center.x - (size.width / 2) let originY = center.y - (size.height / 2) self.init(origin: Point(x: originX, y: originY), size: size) } }
-
Designated Initializers and Convenience Initializers.
-
Designated initializers must always delegate up. Convenience initializers must always delegate across.
-
A designated initializer must call a designated initializer from its immediate superclass.
-
A convenience initializer must call another initializer from the same class.
-
A convenience initializer must ultimately call a designated initializer. It must call
self.init(params...)
which is a designated initializer.
-
-
Swift’s compiler performs four helpful safety-checks to make sure that two-phase initialization is completed without error.
-
4 Safety Check before initialization:
-
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
-
A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.
-
A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.
-
An initializer cannot call any instance methods, read the values of any instance properties, or refer to
self
as a value until after the first phase of initialization is complete.
-
-
Two-Phase Initialization:
-
Phase1:
- A designated or convenience initializer is called on a class.
- Memory for a new instance of that class is allocated. The memory is not yet initialized.
- A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
- The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
- This continues up the class inheritance chain until the top of the chain is reached.
- Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.
-
Phase2:
- Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access
self
and can modify its properties, call its instance methods, and so on. - Finally, any convenience initializers in the chain have the option to customize the instance and to work with
self
.
- Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access
-
-
Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default.
-
You must write the override modifier before the subclass’s initializer definition. This is true even if you are overriding an automatically provided default initializer.
class A { func ab(nameA: String){ println("A") } init(name:String, name2: String) { // same param's Name will be override // by subclass convenience init(name: String, name2: String) } convenience init() { self.init(name: "asa", name2: "asdas") } } class B: A { override func ab(name: String){ println("B") } func ab(name: Int){ // overloading } init(){ super.init(name: "XXXB", name2: "asasdaseee") } // Need override modifier //convenience init(name: String, name2: String) { // self.init() //} // override superclass designated init(name: String, name2: String) override convenience init(name: String, name2: String) { self.init() } }
-
Conversely, if you write a subclass initializer that matches a superclass convenience initializer, that superclass convenience initializer can never be called directly by your subclass.
-
As a result, you do not write the override modifier when providing a matching implementation of a superclass convenience initializer.
-
Subclasses do not inherit their superclass initializers by default. However, superclass initializers are automatically inherited if certain conditions are met.
-
Rule1: If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
-
Rule2: If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition — then it automatically inherits all of the superclass convenience initializers.
-
-
You write a failable initializer by placing a question mark after the init keyword (
init?
). -
A failable initializer creates an optional value of the type it initializes. You write
return nil
within a failable initializer to indicate a point at which initialization failure can be triggered. -
Strictly speaking, initializers do not return a value. Rather, their role is to ensure that
self
is fully and correctly initialized by the time that initialization ends. Although you write return nil to trigger an initialization failure, you do not use thereturn
keyword to indicate initialization success.struct Animal { let species: String init?(species: String) { if species.isEmpty { return nil } self.species = species } } let someCreature = Animal(species: "Giraffe") // someCreature is of type Animal?, not Animal if let giraffe = someCreature { println("An animal was initialized with" + " a species of \(giraffe.species)") } // prints "An animal was initialized with a species of Giraffe"
-
You can use a failable initializer to select an appropriate enumeration member based on one or more parameters.
enum TemperatureUnit { case Kelvin, Celsius, Fahrenheit init?(symbol: Character) { switch symbol { case "K": self = .Kelvin case "C": self = .Celsius case "F": self = .Fahrenheit default: return nil } } } let unknownUnit = TemperatureUnit(symbol: "X") if unknownUnit == nil { println("This is not a defined temperature unit," + " so initialization failed.") } // prints "This is not a defined temperature unit, // so initialization failed."
-
Enumerations with raw values automatically receive a failable initializer,
init?(rawValue:)
, that takes a parameter called rawValue of the appropriate raw-value type and selects a matching enumeration member if one is found, or triggers an initialization failure if no matching value exists.enum TemperatureUnit: Character { case Kelvin = "K", Celsius = "C", Fahrenheit = "F" } let unknownUnit = TemperatureUnit(rawValue: "X") if unknownUnit == nil { println("This is not a defined temperature unit, " + "so initialization failed.") } // prints "This is not a defined temperature unit, // so initialization failed."
-
For classes, however, a failable initializer can trigger an initialization failure only after all stored properties introduced by that class have been set to an initial value and any initializer delegation has taken place.
class Product { let name: String! init?(name: String) { // must provide self.name to set initial value self.name = name if name.isEmpty { return nil } } }
-
A failable initializer of a class, structure, or enumeration can delegate across to another failable initializer from the same class, structure, or enumeration. Similarly, a subclass failable initializer can delegate up to a superclass failable initializer. Use
self.init()
orsuper.init()
.class CartItem: Product { let quantity: Int! init?(name: String, quantity: Int) { self.quantity = quantity super.init(name: name) if quantity < 1 { return nil } } }
-
You can override a failable initializer with a nonfailable initializer but not the other way around.
class Document { var name: String? // this initializer creates a document with a nil name value init() {} // this initializer creates a document with a non-empty name value init?(name: String) { self.name = name if name.isEmpty { return nil } } } class AutomaticallyNamedDocument: Document { override init() { super.init() self.name = "[Untitled]" } // override superclass' failable initializer // Identify by its paramerter list and names. override init(name: String) { super.init() if name.isEmpty { self.name = "[Untitled]" } else { self.name = name } } }
-
You typically define a failable initializer that creates an optional instance of the appropriate type by placing a question mark after the init keyword (
init?
). You can define a failable initializer that creates an implicitly unwrapped optional instance of the appropriate type. (init!
) return non-optional instance instead. -
You can delegate from
init?
toinit!
and vice versa, and you can overrideinit?
withinit!
and vice versa. You can also delegate frominit
toinit!
, although doing so will trigger an assertion if theinit!
initializer causes initialization to fail. -
Write the
required
modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer: -
You must also write the
required
modifier before every subclass implementation of a required initializer.class SomeClass { required init() { // initializer implementation goes here } } class SomeSubclass: SomeClass { required init() { // subclass implementation of the required initializer goes here } }
-
If a stored property’s default value requires some customization or setup, you can use a closure or global function to provide a customized default value for that property.
-
Note that the closure’s end curly brace is followed by an empty pair of parentheses. This tells Swift to execute the closure immediately. If you omit these parentheses, you are trying to assign the closure itself to the property, and not the return value of the closure.
class SomeClass { let someProperty: SomeType = { // create a default value for someProperty inside this closure // someValue must be of the same type as SomeType return someValue }() }
struct Checkerboard { let boardColors: [Bool] = { var temporaryBoard = [Bool]() var isBlack = false for i in 1...10 { for j in 1...10 { temporaryBoard.append(isBlack) isBlack = !isBlack } isBlack = !isBlack } return temporaryBoard }() func squareIsBlackAtRow(row: Int, column: Int) -> Bool { return boardColors[(row * 10) + column] } } // Whenever a new Checkerboard instance is created, // the closure is executed, and the default value of boardColors is // calculated and returned. let board = Checkerboard() println(board.squareIsBlackAtRow(0, column: 1)) // prints "true" println(board.squareIsBlackAtRow(9, column: 9)) // prints "false"
###Reading - SPL: Optional Chaining
-
Optional chaining as an alternative to forced unwrapping.
class Person { var residence: Residence? } class Residence { var numberOfRooms = 1 } // forced unwrapping using ! operator let roomCount = john.residence!.numberOfRooms // this triggers a runtime error when // residence is nil // use optional chaining let roomCount = john.residence?.numberOfRooms
-
The optional chaining attempt returns a "optional Int”. When residence is
nil
, as in the example above, this optionalInt
will also benil
, to reflect the fact that it was not possible to accessnumberOfRooms
. -
The fact that it is queried through an optional chain means that the call to
numberOfRooms
will always return anInt?
instead of anInt
. -
You can use optional chaining with calls to properties, methods, and subscripts that are more than one level deep.
class Person { var residence: Residence? } class Residence { var rooms = [Room]() var numberOfRooms: Int { return rooms.count } subscript(i: Int) -> Room { get { return rooms[i] } set { rooms[i] = newValue } } func printNumberOfRooms() { println("The number of rooms is \(numberOfRooms)") } var address: Address? } let john = Person() if let roomCount = john.residence?.numberOfRooms { println("John's residence has \(roomCount) room(s).") } else { println("Unable to retrieve the number of rooms.") } // prints "Unable to retrieve the number of rooms." let someAddress = Address() someAddress.buildingNumber = "29" someAddress.street = "Acacia Road" john.residence?.address = someAddress // assgined failed cuz john.residence is nil. // will return nil.
-
You can use optional chaining to call a method on an optional value, and to check whether that method call is successful. You can do this even if that method does not define a return value.
-
Functions and methods with no return type have an implicit return type of
Void
. This means that they return a value of()
, or an empty tuple. -
If you call this method on an optional value with optional chaining, the method’s return type will be
Void?
, notVoid
, because return values are always of an optional type when called through optional chaining. -
Any attempt to set a property through optional chaining returns a value of type
Void?
, which enables you to compare againstnil
to see if the property was set successfully.if (john.residence?.address = someAddress) != nil { println("It was possible to set the address.") } else { println("It was not possible to set the address.") } // prints "It was not possible to set the address." println((john.residence?.printNumberOfRooms()).dynamicType) // Swift.Optional<()> println((john.residence?.address).dynamicType) // Swift.Optional<main.Address>
-
Use optional chaining in supscript syntax.
if let firstRoomName = john.residence?[0].name { println("The first room name is \(firstRoomName).") } else { println("Unable to retrieve the first room name.") } // prints "Unable to retrieve the first room name." john.residence?[0] = Room(name: "Bathroom") println(john.residence?[0]) // got nil too
-
If a subscript returns a value of optional type—such as the key subscript of Swift’s
Dictionary
type—place a question mark after the subscript’s closing bracket to chain on its optional return value.var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]] testScores["Dave"]?[0] = 91 testScores["Bev"]?[0]++ testScores["Brian"]?[0] = 72 // the "Dave" array is now [91, 82, 84] // and the "Bev" array is now [80, 94, 81]
-
Linking Multiple Levels of Chaining:
- If the type you are trying to retrieve is not optional, it will become optional because of the optional chaining.
- If the type you are trying to retrieve is already optional, it will not become more optional because of the chaining.
- If you try to retrieve an Int value through optional chaining, an
Int?
is always returned, no matter how many levels of chaining are used. - Similarly, if you try to retrieve an
Int?
value through optional chaining, anInt?
is always returned, no matter how many levels of chaining are used.
if let johnsStreet = john.residence?.address?.street { println("John's street name is \(johnsStreet).") } else { println("Unable to retrieve the address.") } // prints "Unable to retrieve the address." let johnsAddress = Address() johnsAddress.buildingName = "The Larches" johnsAddress.street = "Laurel Street" john.residence!.address = johnsAddress if let johnsStreet = john.residence?.address?.street { println("John's street name is \(johnsStreet).") } else { println("Unable to retrieve the address.") } // prints "John's street name is Laurel Street." // The type of street property is String?. // The return value of john.residence?.address?.street // is therefore also String?
-
Note the use of an exclamation mark during the assignment of an address instance to
john.residence.address
. Thejohn.residence
property has an optional type, and so you need to unwrap its actual value with an exclamation mark before accessing the residence’s address property.
###Reading - SPL: Type Casting
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
// the type of "library" is inferred to be [MediaItem]
-
Use the type check operator (
is
) to check whether an instance is of a certain subclass type. The type check operator returnstrue
if the instance is of that subclass type andfalse
if it is not.var movieCount = 0 var songCount = 0 for item in library { if item is Movie { ++movieCount } else if item is Song { ++songCount } } println("Media library contains \(movieCount)" + "movies and \(songCount) songs") // prints "Media library contains 2 movies and 3 songs"
-
Where you believe this is the case, you can try to downcast to the subclass type with a type cast operator (
as?
oras!
). -
Because downcasting can fail, the type cast operator comes in two different forms.
-
The forced form,
as!
, attempts the downcast and force-unwraps the result as a single compound action. -
This form (
as!
) of the operator will trigger a runtime error if you try to downcast to an incorrect class type.for item in library { if let movie = item as? Movie { println("Movie: '\(movie.name)', dir. \(movie.director)") } else if let song = item as? Song { println("Song: '\(song.name)', by \(song.artist)") } } // Movie: 'Casablanca', dir. Michael Curtiz // Song: 'Blue Suede Shoes', by Elvis Presley // Movie: 'Citizen Kane', dir. Orson Welles // Song: 'The One And Only', by Chesney Hawkes // Song: 'Never Gonna Give You Up', by Rick Astley
-
Swift provides two special type aliases for working with non-specific types:
AnyObject
can represent an instance of any class type.Any
can represent an instance of any type at all, including function types.
-
When working with Cocoa APIs, it is common to receive an array with a type of
[AnyObject]
, or “an array of values of any object type”. This is because Objective-C does not have explicitly typed arrays.let someObjects: [AnyObject] = [ Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"), Movie(name: "Moon", director: "Duncan Jones"), Movie(name: "Alien", director: "Ridley Scott") ] for object in someObjects { let movie = object as! Movie println("Movie: '\(movie.name)', dir. \(movie.director)") } // Or rewrite as for movie in someObjects as! [Movie] { println("Movie: '\(movie.name)', dir. \(movie.director)") }
-
You can use the is and as operators in a switch statement’s cases to discover the specific type of a constant or variable that is known only to be of type
Any
orAnyObject
.var things = [Any]() things.append(0) things.append(0.0) things.append(42) things.append(3.14159) things.append("hello") things.append((3.0, 5.0)) things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman")) // function type -> closure things.append({ (name: String) -> String in "Hello, \(name)" }) for thing in things { switch thing { case 0 as Int: println("zero as an Int") case 0 as Double: println("zero as a Double") case let someInt as Int: println("an integer value of \(someInt)") case let someDouble as Double where someDouble > 0: println("a positive double value of \(someDouble)") case is Double: println("some other double value that I don't want to print") case let someString as String: println("a string value of \"\(someString)\"") case let (x, y) as (Double, Double): println("an (x, y) point at \(x), \(y)") case let movie as Movie: println("a movie called '\(movie.name)'," + "dir. \(movie.director)") case let stringConverter as String -> String: println(stringConverter("Michael")) default: println("something else") } } // zero as an Int // zero as a Double // an integer value of 42 // a positive double value of 3.14159 // a string value of "hello" // an (x, y) point at 3.0, 5.0 // a movie called 'Ghostbusters', dir. Ivan Reitman // Hello, Michael
###Reading - SPL: Nested Types
-
Swift enables you to define nested types, whereby you nest supporting enumerations, classes, and structures within the definition of the type they support.
struct BlackjackCard { // nested Suit enumeration enum Suit: Character { case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣" } // nested Rank enumeration enum Rank: Int { case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten case Jack, Queen, King, Ace struct Values { let first: Int, second: Int? } var values: Values { switch self { case .Ace: return Values(first: 1, second: 11) case .Jack, .Queen, .King: return Values(first: 10, second: nil) default: return Values(first: self.rawValue, second: nil) } } } // BlackjackCard properties and methods let rank: Rank, suit: Suit var description: String { var output = "suit is \(suit.rawValue)," output += " value is \(rank.values.first)" if let second = rank.values.second { output += " or \(second)" } return output } } let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) println("theAceOfSpades: \(theAceOfSpades.description)") // prints "theAceOfSpades: suit is ♠, value is 1 or 11"
-
We can also refer to nested types.
let heartsSymbol = BlackjackCard.Suit.Hearts.rawValue // heartsSymbol is "♡"
###Reading - SPL: Deinitialization
-
Swift automatically deallocates your instances when they are no longer needed, to free up resources.
-
Typically you don’t need to perform manual clean-up when your instances are deallocated. However, when you are working with your own resources, you might need to perform some additional clean-up yourself.
-
If you create a custom class to open a file and write some data to it, you might need to close the file before the class instance is deallocated.
-
Deinitializers are called automatically, just before instance deallocation takes place.
-
You are not allowed to call a deinitializer yourself. Superclass deinitializers are inherited by their subclasses, and the superclass deinitializer is called automatically at the end of a subclass deinitializer implementation. Superclass deinitializers are always called, even if a subclass does not provide its own deinitializer.
-
Deinitializers in action:
struct Bank { static var coinsInBank = 10_000 static func vendCoins(var numberOfCoinsToVend: Int) -> Int { numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank) coinsInBank -= numberOfCoinsToVend return numberOfCoinsToVend } static func receiveCoins(coins: Int) { coinsInBank += coins } } class Player { var coinsInPurse: Int init(coins: Int) { coinsInPurse = Bank.vendCoins(coins) } func winCoins(coins: Int) { coinsInPurse += Bank.vendCoins(coins) } deinit { Bank.receiveCoins(coinsInPurse) } } var playerOne: Player? = Player(coins: 100) println("A new player has joined the game with \(playerOne!.coinsInPurse) coins") // prints "A new player has joined the game with 100 coins" println("There are now \(Bank.coinsInBank) coins left in the bank") // prints "There are now 9900 coins left in the bank" playerOne!.winCoins(2_000) println("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins") // prints "PlayerOne won 2000 coins & now has 2100 coins" println("The bank now only has \(Bank.coinsInBank) coins left") // prints "The bank now only has 7900 coins left" playerOne = nil println("PlayerOne has left the game") // prints "PlayerOne has left the game" println("The bank now has \(Bank.coinsInBank) coins") // prints "The bank now has 10000 coins"
-
The player has now left the game. This is indicated by setting the optional
playerOne
variable tonil
, meaning “no Player instance.” At the point that this happens, theplayerOne
variable’s reference to thePlayer
instance is broken. No other properties or variables are still referring to the Player instance, and so it is deallocated in order to free up its memory.
##To Read List 7/23/2015
- Automatic Reference Counting
- Extensions
- Protocol
- Generics
- Access Control
- Advanced Operators