CS333

Mobile Development


Ilya Loshkarev
loshkarev.i@gmail.com

Functions

Argument labels and Parameter names


              func greet(person name: String, from hometown: String) -> String {
                return "Hello, \(name)! Glad you could visit from \(hometown)!"
              }
              print(greet(person: "Ivan", from: "Rostov"))
            

You can ommit a label by using _


              func greet(_ name: String, _ hometown: String) -> String {
                return "Hello, \(name)! Glad you could visit from \(hometown)!"
              }
              print(greet("Ivan", "Rostov"))
            

Labels allow function to be called in an expressive manner

Think hard before you ommit an argument label

In-Out parameters


              func swapTwo(_ a: inout Int, _ b: inout Int){
                let c = a; a = b; b = c
              }
              var a = 1, b = 2
              swapTwo(&a, &b)
            

All parameters are constant by default

Swift doesn't encourage functions with side effect

Function Types


              func add(_ a: Int, _ b: Int) -> Int {
                return a + b;
              }
              func mult(_ a:  Int, _ b: Int) -> Int {
                return a * b;
              }
              func calc (_ op: (Int, Int) -> Int, _ a: Int, _ b: Int)) {
                print(op(a, b))
              }
              let a = 5, b = 6
              calc(add, a, b)
              calc(mult, a, b)
            

Function's type consists of
types of parameters and return value

Operators


							func +(_ left: Bool, _ right: Bool) {
								return left || right
							}
							let a = true, b = false
							let c = a + b
						

Nested Functions


              func counter(forward: Bool) -> (Int) -> Int {
                func stepForward(input: Int) -> Int {
                  return input + 1
                }
                func stepBackward(input: Int) -> Int {
                  return input - 1
                }

                return forward ? stepForward : stepBackward
              }
            

Closures

Closing over context


              func counter(increment: Int, forward: Bool) -> (Int) -> Int {

                func backward(input: Int) -> Int {
                  return input - increment
                }

                return !forward ? backward :
                  { (input: Int) -> Int in return input + increment }
              }
            

Closures are blocks of code that can capture
local constants and variables

Local context


              // global function
              func counter(increment: Int, forward: Bool) -> (Int) -> Int {
                // local function – increment is captured
                func backward(input: Int) -> Int {
                  return input - increment
                }

                return !forward ? backward :
                // closure expression – increment is captured
                  { (input: Int) -> Int in return input + increment }
              }
            

Local functions and closure expressions
can capture any value in context

Global functions don't capture any values

Closure Expression


              let names = ["Anna", "Ivan", "Maria", "John"]

              var reversedNames = names.sorted(by: {
                  (s1: String, s2: String) -> Bool in
                    return s1 > s2
              })
            

Closure expression – unnamed block of executable code
that can capture values from local context

Brevity of a closure


                var reversedNames = names.sorted(by:
                  { s1, s2 in return s1 > s2 })
              

Parameter types can be inferred from context


                var reversedNames = names.sorted(by: { return $0 > $1 })
              

Parameters can be referenced by shorthand names


                var reversedNames = names.sorted(by: { $0 > $1 })
              

Single-expression closure implicitly returns its result

Even shorter


	              var reversedNames = names.sorted { $0 > $1 }
	            

Closure can be passed with trailing syntax


	              var reversedNames = names.sorted(by: >)
	            

Any operator is a closure

Sharing a variable


              func counter(withIncrement: Int) -> () -> Int {
                var totalCount = 0
                return { totalCount += withIncrement }
              }

              let newCounter = counter(withIncrement: 10)
              print(newCounter()) // 10
              let anotherCounter = newCounter
              print(anotherCounter()) // 20
            

Closures are reference type

Autoclosures


              var names = ["Anna", "Ivan", "Maria", "John"]
              let nextInLine = { names.remove(at:0) }

              print("Next one is \(nextInLine()!") // Anna!
              print(names.count) // 3

              func whosNext(provider nextInLine: @autoclosure () -> String) {
                print("Next one is \(nextInLine())")
              }
              whosNext(provider: names.remove(at:0))
            

@autoclosure automaticaly wraps parameter
in closure expression

Collections

Bridging to Obective-C

  • String
  • Array
  • Dictionary
  • Set
  • NSString
  • NSArray
  • NSDictionary
  • NSSet

Global functions and interfaces from NSFoundation
are availiable in Swift

String Views


							let flag = "🇷🇺"
							print(flag.characters.count)	// Charcter View
							// Prints "1"
							print(flag.unicodeScalars.count) // Unicode View
							// Prints "2"
							print(flag.utf16.count)		// UTF-16 View
							// Prints "4"
							print(flag.utf8.count)		// UTF-8 View
							// Prints "8"
						

Each view has its own indices

String Index


							let start = str.startIndex
							let end = str.endIndex
							let range = start.successor()..<end.advancedBy(-2)
							str.replaceRange(range, with: " swift ")
						

Array


							var doubles: [Double] = []
							var emptyFloats: Array = Array()
							doubles.append(contentsOf: [3.0, 2.0, 1.0])
							doubles += [0.0, -1.0]
							var index = doubles.index(of: 2.0)
							for number in doubles {
								print("\(number)")
							}
						

Dictionary

Set

Classes

Classes and Structures


							struct Point {
								var x = 0.0, y = 0.0
							}

							class Polygon {
								let points: [Point]
								init (_ points: [Point]) {
									self.points = points
								}
								var center: Point {
									/* Calculate center */
									return Point(x: xVal, y:yVal)
								}
							}
						

Classes are reference type

Structures are value type and can not be inherited

Properties


							class Polygon {
								let points: [Point]  // has to be set in initializer
								let name = "polygon" // has a default value
								var area: Double {  // read-only property
									/* Calculate area */
								}
								var origin: Point {
									get {/* do something */}
									set(newOrigin) {/* do something */}
								}
							}
						

Methods


							struct Point {
								mutating func offset(_ xStep: Double, _  yStep: Double) {
									/* Change x, y */
								}
								func distance(_ to: Point) -> Double {
									/* Calculate distance */
								}
							}
							class Polygon {
								func contains(_ p: Point) -> Bool {
									/* do something */
								}
							}
						

Structure methods that change properties has to be marked mutating

Access Control


							class Shady { // implicitly internal
								 private var things: [String] = []
								 private func stuff() {}
								 func allClear() {} // implicitly internal
								 public var totallyClear: String
							}
							private class Classified { // explicitly private
								var nothing: String // implicitly private
							}
						

All classes and class members are implicitly internal

Automatic Reference Counting


							class ARCout {
								init(){
									print ("initialized")
								}
								deinit() {
									print ("deinitialized")
								}
							}
							var reference1: ARCout? = ARCout() // initialized
							var reference2 = reference1
							reference1 = nil
							reference2 = nil // deinitialized
						

Deallocates memory if no references to object exists

Reference Loops


							class ARCout {
								var reference: ARCout?
							}
							var obj1 = ARCout() // initialized
							var obj2 = ARCout() // initialized
							obj1.reference = obj2
							obj2.reference = obj1
							obj1 = nil
							obj2 = nil
							// But nothing happened!
						

							class ARCout {
								weak var reference: ARCout?
							}
						

weak references don't increase retain count

unowned references don't have to be optional values

Inheritance


							class Triangle: Polygon {
								init(_ a: Point, _ b: Point, _ c: Point) {
									let verts = [a,b,c]
									super.init(verts)
									name = "triangle"
								}
								override var area: Double {
									/* Calculate area */
								}
								func inradius: Double {
									/* Calculate incircle radius */
								}
							}
						

super provides access to superclass members

Any overwritten member should be marked with override

Type Casting


							let shapes = [Polygon]()
							/* appended something to shapes */
							for shape in shapes {
								if shape is Triangle {
									print("it's a triangle")
								}
							}

							for shape in shapes {
								if let tri = shape as? Triangle {
									print(tri.inradius)
								}
							}
							

as? wraps result into optional,
returns nil if variable cannot be downcasted

as! triggers a runtime error instead

Error Handling

Throwing Errors


							enum MyErrors: ErrorType {
								case OutOfOptions
							}
							func chooseOption() throws -> Int {
								throw MyErrors.OutOfOptions
							}
						

ErrorType – empty protocol for declaring errors

Only throwing functions can throw and propagate errors

Catching Errors


							do {
								var option = try chooseOption()
							}
							catch MyErrors.OutOfOptions {
								print("I'm all out of options here!")
							}
						

try can only be used in do-catch block

do-catch block stops error propagation
if it catches an error

Other Options


							var option = try? chooseOption() // Int?

							func makeAnOption() -> Int {
								var option = try! chooseOption() // just do it
								return option
							}
						

try? wraps value into an optinal, retruns nil if error occurs

try! fails here and now, stops error popagation

Guard


							guard let option = try? chooseOption() else {
								print("Sorry, we're out of options!")
								return
							}
							print("Congratulations, your option is \(option)")
						

guard is assert-like operator that allow quick escape
if condition is not met

guard-bound optional is accesible outside of it's else clause

Related Resources

Swift-logo