CS333

Mobile Development


Ilya Loshkarev
loshkarev.i@gmail.com

The Basics

Constants and Variables


							let pi = 3.14
							var r = 1.0
							while r < 100 {
								let sqr = r * r * pi
								print("Radius: \(r); Square: \(sqr)")
								r += 1
							}
						
let decalres a constant
var declares a variable

Always declare a constant unless absolutely necessary

Basic Datatypes


							let i: Int = 1  // default Int is either Int32 or Int64
							let f: Float = 2.7
							let d: Double = 3.1
							let b: Bool = true
							let s: String = "hello"
						

Swift can infer type of a variable


								let x = 5, y = 3.142, z = true
								let names = ["Alex", "Anna", "Ivan", "Maria"]
						

Type Conversion

Swift is type-safe language


							var pi = 3 + 0.14 // literals don't have explicit type
							let three = 3
							let pointOneFour = 0.14
							pi = Double(three) + pointOneFour
							// implicit type conversions are not allowed
							let roundPi:Int = Int(pi)
						

All type conversions must be explicit

Functions


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

							print(greet(person: "Ivan", from: "Rostov"))
						

Every parameter has a name and an argument label

Arguments must be labeled when function is called

Optionals


							let someNum = "3.14"
							let number = Double(someNum) // Double?
							if number != nil {
								print("parsed number: \(number!)") // unwraped value is 3.14
							}
						

Optional value contains either value or nil

Always make sure optional contains a value before foced unwraping

Conditional Statement


							let someChar: Character = "z"
							if someChar >= "0" && someChar <= "9" {
								print("This is a digit.")
							} else if someChar >= "a" && someChar <= "z" {
								print("This is a letter.")
							}
						

Curved braces are required

Optional Binding


							let someNum = "3.14"
							if let number = Double(someNum) { // number is Double!
								print("parsed number: \(number)")
							} else {
								print("\(someNum) is not a number") // number is not reachable
							}
						

Bound optional always contains a value

Conditional Statement – Switch


							let someChar: Character = "z"
							switch (someChar) {
								case "a":
									print("It is the first letter of the alphabet")
								case "b"..."y":
									print("It is some other letter of the alphabet")
								case "z":
									print("It is the last letter of the alphabet")
								default:
									print("It is some other character")
							}
						

Case must always have a body

Loops


  							var i = 0
  							while i < 5 {
  								i += 1
  							}
  							repeat {
  								i -= 1
  							} while i > 0
              

  							for i in 0...10 {
  								for j in 0...i {
  									print(".")
  								}
  								print("\n")

  							}
              

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

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