In most cases, you don't need to pick a specific size of integer to use in your code. Swift provides an integer type, Int
, which has the same size as the current platform's native word size:
- On a 32bit platform,
Int
is the same size asInt32
- On a 64bit platform,
Int
is the same size asInt64
let meaningOfLife = 42 // meaningOfLife is inferred to be of type Int
Use the Int
type for all general-purpose integer constants and variables in your code, event if they are known to be non-negative. Using the default integer type in everyday situations means that integer constrants and variables are immediately interoperable in your code and will match the inferred type for integeral literal values without the need of conversion.
Swift provides two signed floating point number types:
Double
represents a 64-bit floating point numberFloat
represents a 32-bit floating point number
Swift always choses Double
(rather than Float
) when inferring the type of floating-point numbers
let anotherPi = 3.13159 // anotherPi is inferred to be of type Double
Type aliases are useful when you want to refer to an existing type by a name that is contextually more appropriate
typealias AudioSample = UInt16
Tuples group multiple values into a single compound value.
if you only need some of the tuple's value, ignore parts of the tuple with an underscore (_)
when you decompose the tuple:
let http404Error = (404, "Not Found")
let (justTheStatusCode, _) = http404Error
Alternatively, access the individual element values ina tuple using index numbers starting at zero:
println("The status code is \(http404Error.0)")
println("The status message is \(http404Error.1)")
You can nume the individual eleemnts in a tuple when the tuple is defined:
let http200Status = (statusCode: 200, description: "OK")
If you name the elements in a tuple, you can use the leemnt names to access the values of those elements:
println("The status code is \(http200Status.statusCode)")
println("The status message is \(http200Status.description)")
Two for loops in swift
- The
for-in
loop performs a set of statements for each item in a sequence - The
for
loop performs a set of statements until a specific condition is met, typically by incrementing a counter each time the loop ends
for index in 1...5 {
println("\(index) times 5 is \(index * 5)")
}
if you don't need each value from a sequence, you can ignore the values by using an underscore in place of a variable name:
for _ in 1...5 {
...
}
You can also iternate over a dictionary
for (key, value) in dictionary {
...
}
You can also iternate a string
for character in "Hello" {
...
}
for var index = 0; index < 10 ; index++ {
...
}
Switch statements do not "fall through" to the next case in Swift, avoiding common C errors caused by missing break
statements
Every switch
statement must be exhaustive. That is, every possible value of the type being considered must be matched by one of the switch
cases. If it is not appropriate to provide a swtich
case for every possible value, you can define a default catch-all case to cover any values that are not addressed explicity. This catch-all case is indicated by the keyword default
, and must always appear last.
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
println("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "s", "t", "v", "w", "x", "y", "z":
println("\(someCharacter) is a consonant")
default:
println("\(someCharacter) is not a vowel or a consonant")
}
Function syntax
func helloWorld() {
println("hello, world!")
}
func add(a: Int, b: Int) -> Int {
return a + b
}
You can call a function without parameter names
func add(a: Int, b: Int) -> Int {
return a + b
}
add(1,2)
If there is a external parameter name, then it must be used
func add(a: Int, toNum b: Int) -> Int {
return a + b
}
add(1, toNum: 2)
If you want the external and internal parameter names to be the same, you don’t have to write out the parameter name twice, just add a # in front of the parameter name as a shortcut
func add(a: Int, #b: Int) -> Int {
return a + b
}
add(1, b: 2)
Calling methods are a bit different. By default swift makes the internal parameter names the external parameter names. The excpetion in the default case is the first parameter where no external parameter name is used.
Hence when you call a method, you need to provide the external parameter name for every parameter except the first. Because of this you typically want to encode the first param name into the method name.
class Foo {
func helloWorldWithName(name: String, surname: String, age: Int) {
println("Hello \(name) \(surname). You're \(age).")
}
}
let foo = Foo()
foo.helloWorldWithName("John", surname: "Citizen", age: 42)
If you want the external name to be excluded then just add _
infront of the internal parameter name
func helloWorldWithName(name: String, _ surname: String, _ age: Int) {
println("Hello \(name) \(surname). You're \(age).")
}
Since functions are first class citizens, you can return functions from functions
func incrementor(amount: Int) -> ((Int) -> Int) {
func adder(num: Int) -> Int {
return num + amount
}
return adder
}
var adder = incrementor(2)
adder(4) // -> 6
You can also pass functions as arguments
func add(adder: (Int) -> Int, num: Int) -> Int {
return adder(num)
}
add(adder, num: 10) // -> 12
Swift provides two ways to resolve strong reference cycles when you work with properties of class type: weak references and unowned references.
Weak and unowned references enable one instance in a reference cycle to refer to the other instance without keeping a strong hold on it.
- Use a weak reference whenever it is valid for that reference to become nil at some point during its lifetime.
- Conversely, use an unowned reference when you know that the reference will never be nil once it has been set during initialization.
- Weak references must be declared as variables, to indicate that their value can change at runtime. A weak reference cannot be declared as a constant.
- Because weak references are allowed to have “no value”, you must declare every weak reference as having an optional type.
- ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated.
- An unowned reference is assumed to always have a value. Because of this, an unowned reference is always defined as a nonoptional type.
- If you try to access an unowned reference after the instance that it references is deallocated, you will trigger a runtime error.
class Foo {
weak var delegate: Bar?
}
You can write a closure without a name by surrounding code with braces ({}). Use in to separate the arguments from the return type.
add({
(num: Int) -> Int in
return num + 2
},
num: 10) // -> 12
if the function is rewritten to use a trailing closure
func add2(num: Int, adder: (Int) -> Int) -> Int {
return adder(num)
}
you can pass the trailing closure after the parentheses
add2(10) { (num: Int) -> Int in
return num + 2
}
If the closure is the only argument to a function, then you can omit the parentheses all together
[3,1,2].sort {(num1: Int, num2: Int) -> Bool in
return num1 < num2
} // [1,2,3]
Single statement closures can omit the return keyword as they implicitly return the value of their only statement
[3,1,2].sort {(num1: Int, num2: Int) -> Bool in
num1 < num2
} // [1,2,3]
When the closure's type is already known then you can omit the type for parameters
[3,1,2].sort {(num1, num2) -> Bool in
num1 < num2
} // [1,2,3]
You can also refer to arguments by numbers to make the closure shorter
[3,1,2].sort { $0 < $1 } // [1,2,3]
You avoid retain cycles in closures by defining a capture list as part of the closure's definition. The capture list defines the rules to use when capturing one or more reference types within the closure's body.
var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
- Define a catpure in aclosure as an unowned reference when the closure and the instance it captures will always refere to each other, and will always be deallocated at the same time. If the capture reference will never become nil, it should always be captured as an unowned reference, rather than a weak reference.
- Conversely, define a capture as a weak reference when the capture reference may become nil at some point in the feature. Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocted
Type cast operator is as
.
as
is used for guareented conversion. E.g for upcastingas!
is used for forced conversions. E.g. for downcasting. Will raise an runtime error if downcast failsas?
is used for optional conversions. Will return nil if it fails.
class Animal {}
class Dog: Animal {}
let a: Animal = Dog()
a as Dog // now raises the error: "'Animal is not convertible to 'Dog';
// ... did you mean to use 'as!' to force downcast?"
a as! Dog // forced downcast is allowed
let d = Dog()
d as Animal // upcast succeeds
let animal: Animal = Cat()
animal as? Dog // evaluates to nil
animal as! Dog // triggers a runtime error
Type type of Swift array is written in full as Array<SomeType>
, where SomeType
is the type of values the array is allowed to store. You can also write the type of an array in shorthand form as [SomeType]
var strArray01 = [String]() // empty string array
strArray01.append("hello") // appending a string
strArray01 += ["world"] // another way to append
strArray01 += ["foo", "bar"] // concat a array of strings
let strArray02 = ["hello", "world"] // initialize array with strings
let strArray03 = strArray01 + strArray02 // create new array from existing arrays
var dict01 = [Int: String]() // empty dictionary
dict01[1] = "One" // add to dictionary
dict01 = [:] // clear dictionary
let dict02 = [1: "One", 2: "Two"] // initialize dictionary with content
- public access enables entities to be used anywhere
- internal access enables entitles to be used within their defining module
- private access restricts use to their own defining source file
All entities in your code have a default access level of internal.
The access control level of a type also affects the default access level of that type's members(its properties methods, initializers, and subscripts).
- If you define a type's access level as private, the default access level of its members will also be private.
- If you define a type's access level as public or internal, the default access level of the type's members will be internal
When working with optional values, you can write ?
before operations like
- methods
- properties
- subscripting
If the value before ?
is nil
, everything after the ?
is ignored and the value of the whole expression is nil
. Otherwise, the optional value is unwrapped, and everything after the ?
acts on the unwrapped value. In both cases, the value of the whole expression is an optional value
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
The nil coalescing operator (a ?? b
) unwraps an optional a
if it contains a value, or returns a default value b
if a
is nil
. The expression b
must match the type that is stored inside a
. It is a shorthand for below
a != nil ? a! : b
You use optional binding
to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable. Optional binding can be used with if
and while
statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a signle action. Multiple optional bindings can appear in a single if
statement as a comma-separated list of assignment expression
if let actualNumber01 = possibleNumber01.toInt(), actualNumber02 = possibleNumber02.toInt() {
println("It is \(actualNumber01) and \(actualNumber02)")
}
If you try to access an implicity unwrapped optional when it does not contain a value, you will trigger a runtime error.
You can use
- Int
- String
- floating point numbers
as the raw type of an enumeration.
enum Rank: Int {
case Ace = 1
case Two, THree, Four
case Jack, QUeen, King
func simpleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.Ace
When assigning a value to the ace constant, the enumeration case Rank.Ace
is referred to by its full name because the constant doesn't have an explicit type specified. Inside the switch, the enumeration case is referred to by the abbreviated form .Ace
because the value of self
is already known to be a Rank
. You can use the abbreviated form anytime the value's type is already known
An instance of an enumeration case can have values associated with the instance.
enum ServerResponse {
case Result(Int, String)
case Error(Int)
}
let success = ServerResponse.Result(200, "Hello World")
let failure = ServerResponse.Error(404)
Here we have 2 values associated with the Result
case and one value with the Error
case. Instances of the same enumeration case can have different values associated with them. Associated values and raw values are different. The raw value of an enumeration case is the same for all of its instances(e.g the raw values above are Result
and Error
)
Structs support many of the same behaviours as classes, including methods and intializers. One of the most important differences between structs and classes is that structs are always copied when they are passed around in your code, but classes are passed by reference.
stuct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescrption = threeOfSpades.simpleDescription()
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
Classes, enumerations, and structs can all adopt protocols
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
the mutating
keyword in the declaration SimpleStructure
to mark a method that modifies the structure. The declaration of SimpleClass
doesn't need any of its methods marked as mutating because methods on a class can always modify the class
Use extensions to add functionality to an existing type, such as
- new methods
- computed properties
- protocol conformance to a type
Write a name inside angle brackets to make a generic function or type
func repeat<Item>(item: Item, times: Int) -> [Item] {
var result = [Item]()
for i in 0..<times {
result.append(item)
}
return result
}
You can make generic forms of:
- functions
- methods
- classes
- enumerations
- structures
// Reimplement the Swift standard library's optional type
enum OptionalValue<T> {
case None
case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibelInteger = .Some(100)
Use where
after the type name to specify a list of requirements - for example:
- to require the type to implement a protocol
- to require two types to be the same class
- or to require a class to have a particular superclass
func anyCommonElements <T, U where T: SequenceType,
U:SequenceType,
T.Generator.Element: Equatable,
T.Generator.Element == U.Generator.Element>
(lhs: T, rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1,2,3], [3])
for simple cases you can shorten the sytnax and exclude the where
. Writing <T: Equatable>
is the same as writing <T where T: Equatable>
###Protocol Extensions
Protocols can be extended to provide method and property implementations to conforming types. This allows you to define behavior on protocols themselves, rather than in each type’s individual conformance or in a global function.
protocl RandomNumberGenerator {
func randomBool() -> Bool
}
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
By creating an extension on the protocol, all conforming types automatically gain this method implementation without any additional modification.
You can use protocol extensions to provide a default implementation to any method or property requirement of that protocol. If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension