This is the note for swift knowledge refreshing.

TYPES:

  1. All types are named with UpperCamelCase. All variables are named with lowerCamelCase.
    • Types: Bool, Int, String, Character… or MyType
    • Variables: num: Int, isTrue: Bool…
  2. Type conversion:
    • type name(value): Double(10)
    • as keyword: let doubleVal = 3 as Double

TUPLES:

When to use a tuple? For shortly operation, like sending multiple values to a function or return multiple values from a function. Otherwise, using struct or class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let coordinate: (Stirng, Int, Int) = ("Coordinate" , 3, 5)
// assign tuples
var (label, x, y) = coordinate
// use _ to disregard one value
var (_, x, y) = coordinate

// access to the value in tuple
let coordinate: (Stirng, Int, Int) = ("Coordinate" , 3, 5)
// use index
coordinate.0 // "Coordinate"
coordinate.1 // 3
coordinate.2 // 5

// or name the value
let coordinate_1 = (name: "Coordinate", x: 3, y: 4)
// use name
coordinate_1.name // "Coordinate"
coordinate_1.x // 3
coordinate_1.y // 5

LOOP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var count = 0

// while loop
// print: 0...3
while count < 4 {
connt += 1
}

count = 0
// repeat while
// print: 1
repeat {
count += 1
} while count > 100

// for loop
for index in 0..<4 {

}

// skip one loop
continue

// leave the loop
break

SWITCHING:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// if you want to fall through the switch case, use fallthrough keyword
switch variable {
case value:
// case code
fallthrough
default:
// default code
}

// case can be a range
let choosed = 2
switch choosed {
case 0...3:
print("You choosed a value between 0 to 3")
default:
break
}

// case can be multiple value
let name = "Jason"
switch name {
case "Jason", "John":
print("You meet Jason and John.")
default:
break
}

// tuple can use swich statement
let north = 0, south = 1, east = 2, west = 3
let movement = (north, 100)
switch movement {
case (south, _):
print("You move to south.")
case (_, 100):
print("You move at 100 miles per hour.")
default:
break
}

// tuple case can be variables
switch movement {
case let (direction, speed) where speed >= 100:
print("You are overspeed.")
default:
break
}

DICTIONARY:

  • Dictionary return optional.
  • Assign nil to delete key pair.
  • Iterate the dictionary
    • for (key, value) in dictionary {}
    • for key in dictionary.keys {}
    • for val in dictionary.values {}

STRUCTURE:

By default, methods in struct can’t mutate the internal variables, because the struct is immutable. But we can label the method to mutating to do the job.

1
2
3
mutating func doSomethingToInternalVal() {
// do something
}

Properties:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct TV {
var height: Double
var width: Double
var diagonals: Int {
// calculated here
return result
}

var spaceTaken: Double {
get {
// calculate here
return result
}
set {
height = Double(newValue)
}
}
}
  • properties: height and width. These can be accessed and mutated.
  • computed properties: diagnonal. These are read only.
  • getter and setter: get and set keywords in a computed property. For setter, we have a newValue keyword to get the setting value.
  • type properties: static properties. We don’t need a instance to get this property, just call the type and reference the property.

CLASS:

When we created an object with “let”, we assigned a constant reference to that variable. You can not assign a new object to it, but you can still change its properties.

Class need initializer when at least one of its properties is not initialed or nil.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Compiler error: Class 'Address' has no initializers
class Address {
var street: String
}

// you can solve by these ways
// add a init function
class Address {
var street: String

init(street: String) {
self.street = street
}
}

// init the street by default
class Address {
var street: String = ""
}

// set street to optional
class Address {
var street: String?
}

INHERITANCE:

final keyword can prevent one class or function been inherited.

INITIALIZERS:

  • Designated initializer need to make sure all none optional properties has a value.
  • Convenience initializer must call designated initializer, however you can set some default values for some properties.
    When calling parent’s initializer, we use super.init(). In swift, we call parent’s initializer after we init all properties in child’s class. This prevents a property been used before it is ready. And more important, we can only call the parent’s designated initializer but not convenience initializer.

PROTOCOL:

Property Requirements

A protocol can require any confirming type to provide an instance property or type property. It only specifies the required property name and type and also specifies whether each property must be gettable or gettable and settable.

1
2
3
4
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}

Always prefix type property requirements with the static keyword when you define them in protocol.

1
2
3
protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
}

Method Requirements

Always prefix type method requirements with the static keyword when you define them in protocol.

1
2
3
protocol SomeProtocol {
static func someTypeMethod()
}

Always prefix mutating method requirements with the mutating keyword when you define them in protocol to indicate that method is allowed to modify the instance it belongs and to any properties of that instance.

When a class confirmed the protocol with mutating method, we don’t need to put mutating keyword before the func. The mutating keyword is only used by structures and enumerations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protocol Togglable {
mutating toggle()
}

enum LightSwitch: Togglable {
case on, off

mutating toggle() {
switch self {
case .on:
self = .off
case .off:
self = .on
}
}
}

Protocol can require specified initializer to be implemented by confirming types. When confirming types implement the initializer, put required keyword before init. If a subclass overrides a designated initializer from a superclass, and also implements a matching initializer requirement from a protocol, mark the initializer implementation with both the required and override modifiers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protocol SomeProtocal {
init()
}

class SomeSuperClass {
init() {
// init implementation
}
}

class SomeSubClass: SomeSuperClass, SomeProtocol {
// "required" from SomeProtocol conformance; "override" from SomeSuperClass
required override init() {

}
}

Protocol as type

  1. As a parameter type or return type in a function, method, or initializer
  2. As the type of a constant, variable, or property
  3. As the type of items in an array, dictionary, or other container

Delegation:

Delegation is a design pattern that enable a class or structure to hand off some of its responsibilities to an instance of another type. The delegation pattern is implemented by:

  1. Define an protocol which contains all delegated responsibilities.
  2. A type confirms the protocol to guarantee to provide all delegated responsibilities.

Delegation can respond to a particular action, or retrive data from an external source without understanding the type of the source.

Protocol Inheritance:

Protocol can inherit one or more other protocols and add further requirements.

Class-only Protocol

We can limit the protocol adoption to class type by adding class keyword after colon.

Optional requirements

Both the protocol and the optional requirement must be marked with the @objc attribute.
Note that @objc protocols can be adopted only by classes that inherit from Objective-C classes or other @objc classes.

1
2
3
4
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}

CUSTOM OPERATORS:

Overloading an existing operator: To overloading an operator, define a new function for the operator symbol.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Use * to repeat a string a specified number of times
// use extension to set what type of variable can use this operator
extension String {
static func *(lhs: String, rhs: Int) -> String {
guard rhs > 0 else {
return ""
}
var res = left
for _ in 0..<right - 1 {
res += left
}
return res
}
}

"a" * 6
// "aaaaaa"

Custom a new operator: A custom operator needs three things.

  1. Define its precedence
  2. Define its associativity: left, right or none.
  3. Define whether its prefix, in fix or postfix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// create exponential operator **
// custom precedence group
precedencegroup ExponentiationPrecedence {
higherThan: MultiplcationPrecedence
associativity: none // none make the programmer use parenthsis
}

// define the operator
infix operator **: ExponentiationPrecedence

// function
func **(base: Double, exponent: Double) -> Double {
return pow(base, exponent)
}

GENERICS

Define generics:

To make your function or type to be generic, add placeholder type name inside angle brackets.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// example
func swapTwoValues<T>(_ a: inout T, _ b: inout T){
let temp = a
a = b
b = temp
}

struct Stack<Element> {
var store = [Element]()
mutating func push(_ item: Element) {
store.append(item)
}
mutating func pop() {
store.removeLast()
}
}

var stackOfInt = Stack<Int>()
stackOfInt.push(1)

Extending a generic type

When we extend a generic type, we don’t need to provide the type parameter in extension definition.

1
2
3
4
5
6
// follow by the generic stack
extension Stack {
var topItem: Element? {
return store.last
}
}

Type constraint syntax

1
2
3
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body
}

Example: we have a function which finds the index number for a generic item in an array.

1
2
3
4
5
6
7
8
func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}

We will see one error in the console, because not every Swift type confirmed the protocol Equatable.

1
error: binary operator '==' cannot be applied to two 'T' operands

To solve this problem, we can make a constraint to the generic type T by adding portable Equatable after T:.

1
2
3
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
// same as above
}

If you want your custom types to use this function, you should make your custom types confirm the protocol Equatable. For example, we make a full name structure, and we have an array of names, find one in it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// make a FullName structure confirming Equatable protocol
struct FullName: Equatable {
var firstName: String
var lastName: String

// Implement the equatable requirement
static func ==(lhs: FullName, rhs: FullName) -> Bool {
return lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName
}
}

// test
let n0 = FullName(firstName: "Jason", lastName: "Han")
let n1 = FullName(firstName: "Dane", lastName: "Fox")
let n2 = FullName(firstName: "Billy", lastName: "Douglas")
let n3 = FullName(firstName: "Fran", lastName: "Deleon")
let n4 = FullName(firstName: "Carolina", lastName: "Buchanan")
let n5 = FullName(firstName: "Natalee", lastName: "Howe")
let names: [FullName] = [n0, n1, n2, n3, n4, n5]

findIndex(of: FullName(firstName: "Billy", lastName: "Douglas"), in: names)
// output: 2

We can also use where clause to define constraints for type.

1
2
3
4
5
6
7
8
9
// The code above
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
// same as above
}

// This can be written as
func findIndex<T>(of valueToFind: T, in array:[T]) -> Int? where T: Equatable {
// same as above
}

Associated Types

An associated type gives a placeholder name to a type that is used as part of the protocol. Assign actual type to that associated type when the protocol is adopted. associatedtype keyword.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Example we made an associated type Item in protocol container
protocol Container {
associatedtype Item

mutating func add(_ item: Item)
var count: Int { get }

subscript(i: Int) -> Item { get }
}

// For a nongeneric type IntStack, we need define the type of Item in IntStack with typealias.
struct IntStack: Container {
var items = [Int]()

// Let Swift know the type of Item
typealias Item = Int

mutating func add(_ item: Int) {
items.append(item)
}

var count: Int {
return items.count
}

subscript(i: Int) -> Int {
return items[i]
}
}

// For a generic type Stack, Swift is smart enough to know the type of the Item.
struct Stack<Element>: Container {
var elements = [Element]()

mutating func add(_ item: Element) {
elements.append(item)
}

var count: Int {
return elements.count
}

subscript(i: Int) -> Element {
return elements[i]
}
}

More for the where clauses we talked above, it can do more jobs for type constraints. This is a function for checking two containers are equal.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {

// Check that both containers contain the same number of items.
if someContainer.count != anotherContainer.count {
return false
}

// Check each pair of items to see if they are equivalent.
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}

// All items match, so return true.
return true
}

In the where clauses:

  1. C1.Item == C2.Item makes sure C1 and C2 contains same type of item.
  2. C1.Item: Equatable makes sure C1’s item confirming equatable protocol, also C2’s item since we checked two containers has same type of items.

Extension with generic where clause

We can use extension with generic where clause to require the element to confirm to a protocol and also require the element to be specific type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Use stack we create above
// We add a function to check whether an element is at the top of stack
extension Stack where Element: Equatable {
func isTop(_ element: Element) -> Bool {
guard let topElement = store.last else {
return false
}
return topElement == element
}
}

// We can specify the element to be double, and calculate the average number of all elements
extension Stack where Element == Double {
func average() -> Double {
var sum = 0.0
for i in 0..<store.count {
sum += store[i]
}
return sum / Double(store.count)
}
}

SUBSCRIPTS:

Classes, structures, and enumerations can define subscripts, which are shortcuts for accessing the member elements of a collection, list, or sequence.

1
2
3
4
5
6
7
8
9
// Example syntax
subscript(index: Int) -> Int {
get {
// return an appropriate subscript value here
}
set(newValue) {
// perform a suitable setting action here
}
}

For usage, we use [] after the type. Take dictionary as a example,

1
2
3
let numberOfPages = ["The Lord of the Rings" : 900, "The Man Without Qualities" : 289]
numberOfPages["The Man Without Qualities"]
// print 289