Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Quick intro to Swift - language

Quick intro to Swift - language

Quick intro to Swift - language

D9e65f4b0af059ae9ba243c8c2265e4f?s=128

Jussi Pohjolainen

October 28, 2020
Tweet

Transcript

  1. Quick Intro to Swift - language Jussi Pohjolainen

  2. Swift • Swift is a general purpose, multi-paradigm, compiled programming

    language • Developed by Apple • iOS, iPadOS, macOS, watchOS, tvOS • Also available for Linux and Windows • Replacement for Obj-C • Swift is open source • https://swift.org/ • Just download Xcode on Mac and you are ready to go
  3. Hello World in Obj-C $ ls helloworld.m $ cat helloworld.m

    // First program example #import <Foundation/Foundation.h> int main (int argc, const char * argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSLog (@"Hello, World!"); [pool drain]; return 0; } $ clang -framework foundation helloworld.m $ ls a.out helloworld.m $ ./a.out 2015-03-05 09:26:00.678 a.out[40432:5270985] Hello, World!
  4. Hello World in Swift! That was Easy! > ls helloworld.swift

    > cat helloworld.swift print("Hello, world!") > swift helloworld.swift Hello, world!
  5. Compiling in Swift > ls helloworld.swift > cat helloworld.swift print("Hello,

    world!") > swiftc helloworld.swift > ./hello Hello, world!
  6. Swift Types

  7. Types • Named type • Type that has a name

    • Classes, structures, enumerations, protocols, arrays, dictionaries • "Primitive types" are also named types (numbers, characters, strings) • You can extend the behaviour of these using extensions • Compound types • Type without a name • Function types and tuple types • Function type example: • ((Int, Int)) -> Void • All types are "non primitive"
  8. Named Basic Types Types with name

  9. Named Types • Swift has it's own versions of C

    / Obj-C types: • Int, Double, Float, Bool, String .. • Also collection types: • Array and Dictionary • Also a concept called optional types • Swift is a type – safe language, you cannot pass a String to an int
  10. Float Documentation It's a struct!

  11. And it has methods

  12. Example let x : Float = 1.12 let y =

    x.rounded() print(y)
  13. Constants (let) and Variables (var) • To declare a a

    constant, use keyword let let myConstant = 10 • To declare a variable, use keyword var var myVariable = 20 • You can do this in on line var x = 0, y = 0, z = 0 • Use Type Annotation to declare a type var hello: String • It's rare you need to use type annotation; variable type is given in initial value. • You can use unicode! let π = 3.14159 let  = "" let = "dogcow"
  14. Comparison between common languages Swift Java Kotlin ECMAScript Variable var

    x = 5 int x = 5 var x = 5 let x = 5 Constant variable let PI = 3.14 final double PI = 3.14 val PI = 3.14 const PI = 3.14
  15. Comments • Comments very similar than in C • One

    line // one line • Multiline /* multiline multiline */ • Can be also nested (unlike in C) /* You can use /* nested comments */ like this */
  16. Numbers and Boolean Integers Types (these are also structs) •

    Int8, Int16, Int32, Int64 • UInt8, UInt16, UInt32, UInt64 Integer Bounds let minValue = UInt8.min // 0 let maxValue = UInt8.max // 255 You can also just use Int • On 32-bit platform, Int -> Int32 • On 64-bit platform, Int -> Int64 Floating Point Numbers Types • Double for 64-bit • Float for 32-bit Boolean • Bool is either true or false
  17. Type Safety • Swift is very clear about types, If

    it's String you cannot pass a Int • You don't have to declare type, but it has a type! let a = 5 // It's an Int! let π = 3.14 // It's a Double! • You can cast Integers let one: UInt8 = 1 let bigger : UInt16 = UInt16(one) • And floating – points let pi = Double(three)
  18. Compound Type: tuple

  19. Tuples • Tuples group multiple values into a single compound

    value let contact : (String, Int) = ("Jack", 123456) print(type(of: contact)) • You can decompose let (name, number) = contact • If you want the other, use underscore let (name, _) = contact • Indexes also available var name = contact.0 • Naming let contact : (name: String, number: Int) = (name: "Jack", number: 123456) print(contact.name) • Useful in functions that returns multiple values
  20. Optionals

  21. Optionals: ? • Add ? to the end of the

    type to declare a optional chaining • This means that the variable may hold also value nil: // x may be integer OR nil var x : Int? • If you make calculations, this fails because x may hold nil! var sum = x + 5 • You can unwrap the ? from the variable using ! var sum = x! + 5 • Now it crashes runtime if x holds nil, but it compiles
  22. class StockExchange { public static String findStockCode(String company) { if(company.equals("Nokia"))

    { return "NOK"; } else if (company.equals("Apple")) { return "AAPL"; } else { return null; } } } class MyApp { public static void main(String [] args) { String code = StockExchange.findStockCode( args[0] ); System.out.println( code.toLowerCase() ); } } What is a possible error here? Running Java app: java MyApp Microsoft
  23. class StockExchange { class func findStockCode(company : String) -> String?

    { if(company == "Nokia") { return "NOK" } else if (company == "Apple") { return "AAPL" } else { return nil } } } var code : String? = StockExchange.findStockCode( company: CommandLine.arguments[1] ); print(code.lowercased()) error: value of optional type 'String?' must be unwrapped to refer to member 'lowercased' of wrapped base type 'String' print(code.lowercased())
  24. Checking and Unwrapping class StockExchange { class func findStockCode(company :

    String) -> String? { if(company == "Nokia") { return "NOK"; } else if (company == "Apple") { return "AAPL"; } else { return nil; } } } var code : String? = StockExchange.findStockCode( company: CommandLine.arguments[1] ); if code != nil { print(code!.lowercased()); } Checking for null values. Also ! is mandatory here.
  25. Conditional unwrapping class StockExchange { class func findStockCode(company : String)

    -> String? { if(company == "Nokia") { return "NOK"; } else if (company == "Apple") { return "AAPL"; } else { return nil; } } } var code : String? = StockExchange.findStockCode( company: CommandLine.arguments[1] ); if let checkedCode = code { print(checkedCode.lowercased()); } It will unwrap the conditional of the code variable
  26. Example let optionalValue1 : Int? = Bool.random() ? 5 :

    nil let optionalValue2 : Int? = Bool.random() ? 5 : nil if let value1 = optionalValue1 { if let value2 = optionalValue2 { print("\(value1 + value2)") } }
  27. With one line let optionalValue1 : Int? = Bool.random() ?

    5 : nil let optionalValue2 : Int? = Bool.random() ? 5 : nil if let value1 = optionalValue1, let value2 = optionalValue2 { print("\(value1 + value2)") }
  28. class Stock { var code: String? var price: Double? }

    class StockExchange { class func findStockCode(company : String) -> Stock? { if(company == "Nokia") { let nok = Stock() nok.code = "NOK" nok.price = 6.5 return nok; } else if (company == "Apple") { let aapl = Stock() aapl.code = "AAPL" return aapl; } else { return nil; } } } if let stock = StockExchange.findStockCode( company: CommandLine.arguments[1] ) { if let price = stock.price { print(price) } } We will have multiple conditional unwrapping here... We will have multiple conditional unwrapping here...
  29. class Stock { var code: String? var price: Double? }

    class StockExchange { class func findStockCode(company : String) -> Stock? { if(company == "Nokia") { let nok = Stock() nok.code = "NOK" nok.price = 6.5 return nok; } else if (company == "Apple") { let aapl = Stock() aapl.code = "AAPL" return aapl; } else { return nil; } } } if let stock = StockExchange.findStockCode( company: CommandLine.arguments[1] ) { if let price = stock.price { print(price) } } We will have multiple conditional unwrapping here... We will have multiple conditional unwrapping here...
  30. class Stock { var code: String? var price: Double? }

    class StockExchange { class func findStockCode(company : String) -> Stock? { if(company == "Nokia") { let nok = Stock() nok.code = "NOK" nok.price = 6.5 return nok; } else if (company == "Apple") { let aapl = Stock() aapl.code = "AAPL" return aapl; } else { return nil; } } } if let stock = StockExchange.findStockCode( company: CommandLine.arguments[1] ), let price = stock.price { print(price) } With one line
  31. class Stock { var code: String? var price: Double? }

    class StockExchange { class func findStockCode(company : String) -> Stock? { if(company == "Nokia") { let nok = Stock() nok.code = "NOK" nok.price = 6.5 return nok; } else if (company == "Apple") { let aapl = Stock() aapl.code = "AAPL" return aapl; } else { return nil; } } } if let price = StockExchange.findStockCode( company: CommandLine.arguments[1] )?.price { print(price) } Or...
  32. Example class Person { var pet : Pet? = Bool.random()

    ? Pet() : nil } class Pet { var favoriteToy : String? = Bool.random() ? "Ball" : nil } let tina : Person? = Bool.random() ? Person() : nil if let t = tina, let p = t.pet, let f = p.favoriteToy { print(f) }
  33. ... or class Person { var pet : Pet? =

    Bool.random() ? Pet() : nil } class Pet { var favoriteToy : String? = Bool.random() ? "Ball" : nil } let tina : Person? = Bool.random() ? Person() : nil if let f = tina?.pet?.favoriteToy { print(f) }
  34. Using ! or ? let toy1 : String? = tina?.pet?.favoriteToy

    let toy2 : String = tina!.pet!.favoriteToy!
  35. Downcasting using as! or as? • To downcast an object

    to subtype, use • as? = If downcast was unsuccessful, put nil to the type • as! = If downcast was unsuccessful, crash • as? - example • var number1 = dict[2] as? Int • println(number1) // optional(7) • as! - example • var number2 = dict[2] as! Int • println(number2) // 7
  36. Optionals: ! • In addition to creating optional with ?

    you can use ! • By using ? this will NOT work • let y : Int? = 12 • print(y + y) • Either check if not null OR use ! (=will crash if nil) • let y : Int? = 12 • print(y! + y!) • If you have to mark ! in everywhere, shortcut • let y : Int! = 12 • print(y + y) • The y is now optional, but you do not have to unwrap it everytime
  37. Example let x : Int? = Bool.random() ? 5 :

    nil print(x! + x!) let x : Int! = Bool.random() ? 5 : nil print(x + x) Crash if nil values Crash if nil values
  38. Lab

  39. Some Control Structures

  40. if var grade = 0 if grade == 0 {

    print("fail") } No need for (...)
  41. while var i = 0 while i < 10 {

    print("\(i)") i += 1 }
  42. repeat while var i = 0 repeat { print("\(i)") i

    += 1 } while i < 10
  43. What...?

  44. for - loop for index in 1...5 { print("\(index)") }

    for index in 1..<5 { print("\(index)") } for _ in 1...5 { print("hello") }
  45. for - loop // 0, 2, 4, 6, 8 for

    index in stride(from: 0, to: 10, by: 2) { print("\(index)") } // 0, 2, 4, 6, 8, 10 for index in stride(from: 0, through: 10, by: 2) { print("\(index)") }
  46. Functions

  47. Defining a Function • Simple example about function func sayHello(personName:

    String) -> String { let greeting = "Hello, " + personName + "!" return greeting } • Return type is indicated using the return arrow -> • Using parameters is easy func sum(start: Int, end: Int) -> Int { return start + end } • You can return several values using Tuples func doSomething(start: Int, end: Int) -> (start: Int, end: Int) { return (start, end) }
  48. Calling a Function • Does not work! func sum(start: Int,

    end: Int) -> Int { return start + end } print( sum(5,5) ) • Change... print( sum(start: 5, end:5) )
  49. Function Parameter Names • Problem: What is the purpose of

    the given parameters? join("Hello", "World", ","); • Solution join(string: "Hello", and: "World", withJoiner: ",") • How? func join(string: String, and string2: String, withJoiner joiner: String) { ... }
  50. Omitting External Name • It's possible to omit external name

    using _ func join(_ string: String, and string2: String, withJoiner joiner: String) -> String { return string1 + joiner + string2; } print( join("two", and: "three", withJoiner: "+") );
  51. Default Parameter Names • Func definition func findInArray(myArray: [Int], value

    value: Int = 0) -> Int { .. } • Function calling findInArray(myArray: array, value: 2); findInArray(myArray: array);
  52. Variadic Parameters • A variadic parameter accepts zero or more

    values of a specified type • Use ... after parameters name func giveNumbers(numbers: Int...) { } • And now you can use the function giveNumbers(numbers: 4,3,2) giveNumbers(numbers: 4)
  53. inout parameters • Objects/functions are reference types, others value types

    • To pass a reference of an variable, use inout func passNumber(number: inout Int) { number = 1; } • And usage var number = 4 passNumber(number: &number) print("\(number)")
  54. Function Types • Functions are variables. Declare a variable with

    function type! var mathFunction: (Int, Int) -> Int • Define two different functions func sum(a: Int, b: Int) -> Int { return a + b; } func extract(a: Int, b: Int) -> Int { return a - b; } • And usage var myVar: (Int, Int) -> Int myVar = sum myVar = extract • Function can be a parameter and return type!
  55. Closures • Closures are like lambdas in Java / C#

    or blocks in Obj-C • Like mini-functions without a name • Syntax { (parameters) -> returntype in statements }
  56. Passing Function to a function func fun(msg: String) { print(msg)

    } func isPositiveInteger(number : Int, success: (String) -> Void) { if(number > 0) { success("it was positive") } } isPositiveInteger(number: 5, success: fun)
  57. Closure Expression Syntax func isPositiveInteger(number : Int, success: (String) ->

    Void) { if(number > 0) { success("it was positive") } } isPositiveInteger(number: 5, success: {(msg: String) -> Void in print(msg) }) Passing closure
  58. Closure Expression Syntax func isPositiveInteger(number : Int, success: (String) ->

    Void) { if(number > 0) { success("it was positive") } } isPositiveInteger(number: 5, success: {msg -> Void in print(msg) }) Omit type
  59. Closure Expression Syntax func isPositiveInteger(number : Int, success: (String) ->

    Void) { if(number > 0) { success("it was positive") } } isPositiveInteger(number: 5, success: {msg in print(msg) }) Omit return type
  60. Shorthand Argument name func isPositiveInteger(number : Int, success: (String) ->

    Void) { if(number > 0) { success("it was positive") } } isPositiveInteger(number: 5, success: { print($0) } ) Output the first argument
  61. Trailing Closure func isPositiveInteger(number : Int, success: (String) -> Void)

    { if(number > 0) { success("it was positive") } } isPositiveInteger(number: 5) { print($0) } If final argument is function, you can use also trailing syntax
  62. Trailing Closure func isPositiveInteger(number : Int = 5, success: (String)

    -> Void) { if(number > 0) { success("it was positive") } } isPositiveInteger() { print($0) } Default value usage
  63. Trailing Closure func isPositiveInteger(number : Int = 5, success: (String)

    -> Void) { if(number > 0) { success("it was positive") } } isPositiveInteger { print($0) } With no arguments we can omit ()
  64. SwiftUI Example HStack { Text("Target Color Block") Text("Target Color Block")

    } Trailing Closure Usage here...
  65. Array Examples

  66. Usage: external Function func modify(item: String) -> String { return

    "<li>" + item + "</li>" } var shoppingList: [String] = ["Eggs", "Milk"] var htmlUI : [String] = shoppingList.map(modify) print(htmlUI)
  67. Usage: Closure var shoppingList: [String] = ["Eggs", "Milk"] var htmlUI

    : [String] = shoppingList.map({item -> String in return "<li>" + item + "</li>" }) print(htmlUI)
  68. Usage: Closure var shoppingList: [String] = ["Eggs", "Milk"] var htmlUI

    : [String] = shoppingList.map({ return "<li>" + $0 + "</li>" }) print(htmlUI)
  69. Usage: Closure, trailing var shoppingList: [String] = ["Eggs", "Milk"] var

    htmlUI : [String] = shoppingList.map() { return "<li>" + $0 + "</li>" } print(htmlUI)
  70. Usage: Closure, automatic return var shoppingList: [String] = ["Eggs", "Milk"]

    var htmlUI : [String] = shoppingList.map() { "<li>" + $0 + "</li>" } print(htmlUI)
  71. String interpolation var shoppingList: [String] = ["Eggs", "Milk"] var htmlUI

    : [String] = shoppingList.map() { "<li>\($0)</li>" } var htmlUIString : String = htmlUI.joined(separator: "") print(""" <ul> \(htmlUIString) </ul> """)
  72. Strings and Arrays

  73. String Creation var stringA = "Hello" print( stringA ) var

    stringB = String("Hello") print( stringB ) let stringC = """ Hello Hello """ print(stringC)
  74. String Interpolation let a = 2 var c = 2.2

    var stringA = "it's easy to embed variables like a = \(a) and c = \(c)" print( stringA )
  75. Iteration and equal var varA = "Hello, World!" var varB

    = "Hello, World!" if varA == varB { print("equal") } print(varA.count) for char in varA { print(char) }
  76. Collection Types: Array • Array var shoppingList: [String] = ["Eggs",

    "Milk"] var shoppingList = ["eggs", "Milk"] var someInts = [Int]() • Length of an Array shoppingList.count • Appending shoppingList.append("bread") shoppingList += ["Tomato"] • Retrieve shoppingList[0] • Change range shoppingList[0...3] = ["bananas", "apples", "coffee"] • Insert shoppingList.insert("something", atIndex:0)
  77. Iterating Array • for-in loop for item in shoppingList {

    println(item) } for item in 1...5 { println(item) } for _ in 1...5 { } • Enumerate for(index, value) in enumerate(shoppingList) { println(index) println(value) }
  78. Collection Types: Dictionary • Dict: key – value pairs •

    Key is unique, no spesified order • Usage var dictionary : [String: String] = ["key1": "value1", "key2": "value2"] var dictionary = [String: String]() • Append dictionary["key3"] = "value3" • Remove dictionary.removeValueForKey("key3") • Iterating for(key, value) in dictionary { ... } • Access keys or values dictionary.keys dictionary.values Unlike tuple, this must have keys and items can be added and removed dynamically
  79. Ranges let range: ClosedRange = 0...10 print(range.first!) // 0 print(range.last!)

    // 10 for index in range { print(index) } let names = ["Jack", "Tina", "Paul"] print(names[0...1]) let range2: Range = 0..<10 print(range2.first!) // 0 print(range2.last!) // 9 print(names[0..<2]) Type is ClosedRange Type is Range
  80. OO with Swift

  81. Classes vs Structures • Structures • Properties (Attributes) and functions

    (methods) • Initializers (Constructors) • Can conform to a protocol (Interface) • Can be extended using extension • Value type • Class can have additional capabilities • Can be inherited • Deinitializers available • Can be referenced more than one time • Reference type
  82. Moving from Classes to Structs • SwiftUI heavily uses Structs

    over Classes • Classes become bloated because of inheritance (look at UIView) • You can extend structs with extension • Struct instances are allocated on stack, drastically faster • In multithreaded environment structs are safer (value type) • When making changes to a struct it does not influence any other part of the app (value type)
  83. Struct struct Person { var name: String = "" var

    age: Int = 0 } var jack = Person() jack.name = "Jack" jack.age = 30 print("\(jack.name)")
  84. Comparing Structs struct Person { var name: String = ""

    var age: Int = 0 } var a = Person() a.name = "Jack" a.age = 30 var b = Person() b.name = "Jack" b.age = 30 print(a == b) helloworld.swift:14:9: error: binary operator '==' cannot be applied to two 'Person' operands print(a == b)
  85. Comparing Structs: global function == func == (left: Person, right:

    Person) -> Bool { return (left.name == right.name) && (left.age == right.age) } struct Person { var name: String = "" var age: Int = 0 } var a = Person() a.name = "Jack" a.age = 30 var b = Person() b.name = "Jack" b.age = 30 print(a == b) Comparison now works
  86. Equatable protocol struct Person: Equatable { var name: String =

    "" var age: Int = 0 } var a = Person() a.name = "Jack" a.age = 30 var b = Person() b.name = "Jack" b.age = 30 print(a == b) Equatable protocol provides default == function to the struct
  87. Equatable protocol struct Person: Equatable { var name: String =

    "" var age: Int = 0 static func == (left: Person, right: Person) -> Bool { return (left.name == right.name) } } var a = Person() a.name = "Jack" a.age = 40 var b = Person() b.name = "Jack" b.age = 30 print(a == b) Possible to override the default implementation
  88. Using Class class Person: Equatable { var name: String =

    "" var age: Int = 0 static func == (left: Person, right: Person) -> Bool { return (left.name == right.name) } } var a = Person() a.name = "Jack" a.age = 40 var b = Person() b.name = "Jack" b.age = 40 print(a === b) print(a == b) ?
  89. Properties • Properties are variables or constants in classes, structs

    or enumerations. • Stored properties • let property: (Int) • var property: (Int) • Computated properties • calculate the value of property using get and set • Property observers • Monitor changes in property's value • Every property needs to be assigned a value either during declaration or in the initializer
  90. Stored Properties struct Point { var x : Int var

    y : Int init(x: Int, y: Int) { self.x = x self.y = y } } var a = Point(x:0, y:0) a.x = 9 print(a)
  91. Computated Properties • Computated properties does not store a value!

    • Provide getter and setter (optional) that calculates some value • The property value is meant to be computed from other instance properties! Computated property is not in memory
  92. class Time { var seconds: Double = 0 init(seconds: Double)

    { self.seconds = seconds } var minutes: Double { get { return (seconds / 60) } set { self.seconds = (newValue * 60) } } } var t = Time(seconds: 60) print(t.seconds) t.minutes = 90 print(t.seconds) print(t.minutes)
  93. struct Person { private var _age : Int = 0

    init(age: Int) { self.age = age } var age: Int { set { if(newValue > 0) { self._age = newValue } } get { return self._age } } } var t = Person(age: 20) t.age = -80 print(t.age) // 20 This will invoke setter
  94. Static struct Circle { static var PI : Double =

    3.14 static func calculateArea(radius: Double) -> Double { return Circle.PI * radius * radius; } } print(Circle.calculateArea(radius: 5)) // Won't work // var c = Circle() // print(c.calculateArea(radius: 5))
  95. Class Inheritance, overriding: static class BaseClass { static func someStaticMethod()

    -> Void { print("someStaticMethod") } } class ChildClass : BaseClass { override static func someStaticMethod() -> Void { print("overriden method") } } // error: cannot override static method ChildClass.someStaticMethod()
  96. Class Inheritance, overriding: class class BaseClass { class func someStaticMethod()

    -> Void { print("someStaticMethod") } } class ChildClass : BaseClass { override static func someStaticMethod() -> Void { print("overriden method") } } // Works! ChildClass.someStaticMethod()
  97. Mutating Struct struct Person { var weigth : Int =

    1 mutating func eat() { self.weigth += 1 } } var jack = Person(weigth: 50) jack.eat() If Struct modifies itself, add mutating Struct has automatic init for public properties
  98. Mutating Struct struct Person { var weigth : Int =

    1 mutating func eat() { self.weigth += 1 } } let jack = Person(weigth: 50) jack.eat() Will fail! cannot use mutating member on immutable value: 'jack' is a 'let' constant
  99. Mutating Class class Person { var weigth : Int =

    1 func eat() { self.weigth += 1 } } let jack = Person() jack.weigth = 40 jack.eat() mutating not needed No automatic init, you can use let here
  100. Lab

  101. Inheritance, Initialization, Deinitialization

  102. Inheritance • Class can inherit methods, properties and other from

    another class • Inheriting class is subclass, class it inherites is superclass • Class that does not inherit from another class is base class • Swift classes do not inherit from universal base class! • You can use AnyObject, that can represent an instance of any type • Classes can add property observers to inherited properties
  103. Class Inheritance class Human { var name : String =

    "" func sleep() { print("\(name) is sleeping") } func drink() { print("\(name) is drinking water") } func printMyInfo() { print("name = \(name)") } } class Programmer : Human { var salary : Int = 0 func implementCode() { print("\(name) is working") } override func drink() { print("\(name) is drinking energy drink") } override func printMyInfo() { super.printMyInfo() print("salary = \(salary)") } } var p = Programmer() p.salary = 4000 p.name = "jack" p.drink() p.printMyInfo()
  104. You can override properties too class ProjectManager { var salary

    : Int = 5000 func drink() { print("Project Manager is drinking water") } } class Programmer : ProjectManager { override var salary : Int { set { super.salary = newValue } get { return super.salary - 1000 } } override func drink() { print("Programmer is drinking energy drink") } } var p = Programmer() p.salary = 4000 print(p.salary)
  105. Initialization • init – methods are special methods that can

    be called when creating a new instance of particular type • Purpose is to ensure that objects are correctly initialized • Also deinitialization methods available • Classes and structures must set all stored properties when object is created • When setting the value in init, property is directly set, no observations are called • Swift provides automatic external name for every parameter in init. • External names must be used when using the init
  106. Simple (not working) init struct Color { var red, green,

    blue: Double init() {} } let halfGray = Color() Will fail, all properties must be initialized
  107. Compiles! Why? struct Color { var red, green, blue: Double?

    init() {} } let halfGray = Color() Optional Double, now it works..
  108. Compiles too.. struct Color { var red, green, blue :

    Double init() { self.red = 0.0 self.green = 0.0 self.blue = 0.0 } } let halfGray = Color()
  109. Using Default values struct Color { var red = 0.0

    var green = 0.0 var blue = 0.0 init() {} } let halfGray = Color() If a property always takes the same initial value, provide a default value rather than setting a value within an initializer. It makes for shorter, clearer initializers and enables you to infer the type of the property from its default value.
  110. Initalizers with init parameters struct Color { var 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 } } let magenta = Color(red: 1.0, green: 0.0, blue: 1.0) let halfGray = Color(white: 0.5)
  111. External Names struct Color { var red, green, blue :

    Double init(w white : Double) { self.red = white self.green = white self.blue = white } } let halfGray = Color(w: 0.5)
  112. Designated Initializers and Convenience Initializers in Classes • Designated Initializers

    • Primary initializers for a class / struct • Initializes all properties • Calls superclasses initializer • Every class must have at least one designated initializer • Use init(parameters) { .. } • Convenience Initializers • Secondary supporting initializers • You do not have to provide these • Use convenience init(parameters) { ... }
  113. Rules Image from Apple Swift documentation

  114. SImple Designated Init class Motor { } class Vehicle {

    var brand: String init(brand: String) { self.brand = brand } } class Car : Vehicle { var motor: Motor init(brand : String, motor: Motor) { self.motor = motor super.init(brand: brand) } override convenience init(brand : String) { let m = Motor() self.init(brand: brand, motor: m) } } var c1 = Car(brand: "Porsche", motor: Motor()) var c2 = Car(brand: "Skoda")
  115. Automatic Initializer Inheritance • Subclasses do not inherit initializers by

    default • If certain conditions are met, initializers are automatically inherited • Rule 1 • If your subclass doesn't define designated initializer, it automatically inherits all of its superclass designated initializers • Rule 2 • If your subclass provides an implementation of all of its superclasses designated initializers (either rule 1 or creating new ones) then it automatically inherites all of the superclass convenience initializers
  116. Example about Automatic Inheritance class A { var x :

    Int var y : Int init(x: Int, y: Int) { print("Designated A init()"); self.x = x self.y = y } convenience init() { print("Convenience A init()"); self.init(x: 0, y: 0) } } class B : A {} var b = B() Rule 1: If your subclass doesn't define designated initializer, it automatically inherits all of its superclass designated initializers Rule 2: If your subclass provides an implementation of all of its superclasses designated initializers (either rule 1 or creating new ones) then it automatically inherites all of the superclass convenience initializers Does it work?
  117. Safety Check 1 class A { var x : Int

    var y : Int init(x: Int, y: Int) { print("Designated A init()"); self.x = x self.y = y } convenience init() { print("Convenience A init()"); self.init(x: 0, y: 0) } } class B : A { var z : Int init(x: Int, y: Int, z: Int) { print("Designated B init()") self.z = z; super.init(x: x, y: y) } } var b = B(x: 0, y:0, z:0) Own properties must be initialized first!
  118. Safety Check 2 class A { var x : Int

    var y : Int init(x: Int, y: Int) { print("Designated A init()"); self.x = x self.y = y } convenience init() { print("Convenience A init()"); self.init(x: 0, y: 0) } } class B : A { var z : Int init(x: Int, y: Int, z: Int) { print("Designated B init()") self.z = z; super.init(x: x, y: y) self.x = 0 } } var b = B(x: 0, y:0, z:0) If accessing base classes properties, do it after super.init
  119. Overriding init class A { var x : Int init(x:

    Int) { self.x = x } } class B : A { var y : Int override init(x: Int) { self.y = 0 super.init(x:0) } init(x: Int, y: Int) { self.y = y super.init(x: x) } } notice the override
  120. Memberwise init for Structs struct Rectangle { var width =

    0 var height = 0 } let r1 = Rectangle(width: 3, height: 4) let r2 = Rectangle(height: 4) Default inits if no init given
  121. Failable init struct Person { private var _age : Int

    init?(age: Int) { if age > 0 { self._age = age } else { return nil } } } if let s = Person(age: 7) { print("\(s)") } can return nil
  122. Required init class Person { init() { } } class

    Programmer : Person { init(salary: Int) { } } let a = Programmer(salary: 4000) //let b = Programmer() Programmer has only one init!
  123. Required init class Person { required init() {} } class

    Programmer : Person { init(salary: Int) {} required init() {} } let a = Programmer(salary: 4000) let b = Programmer() Subclasses must have
  124. Protocols

  125. Callbacks • Several ways of making callbacks • Closures •

    Anonymous functions that have access to local variables • Selectors (kind of depricated in Swift) • Function pointer passing functions. Not type-safe and it's obj-c feature. API uses it, so for legacy reasons, swift provides a way of passing selector function. • Delegate Protocols • Several callback methods!
  126. Selector class SelectorTest { func main { let selector :

    Selector = #selector(self.someFunc) self.test(selector); } func test(function: Selector) { performSelectorOnMainThread(function, withObject: self, waitUntilDone: true) } func someFunc(x : Int) { NSLog("Hello World"); } }
  127. Closures • Closures are like lambdas in Java / C#

    or blocks in Obj-C • Like mini-functions without a name • Example 1 reversed = sorted(names, { (s1: String, s2: String) -> Bool in return s1 > s2 }) • Example 2 func hello(a : Int, b: Int) -> Void { println(a + b) } func testClosures(m : (Int, Int) -> Void) { m(5,5) } testClosures(hello);
  128. Protocols • Define a blueprint of • methods • properties

    • Protocol can be adopted by a class, struct or enumeration • .. to provide the actual implementation
  129. Protocols and Delegate • Delegate refers to Delegation Design Pattern

    that uses protocols (let’s see this in a bit..) • Protocol is an interface that holds method declarations • When a class conforms to a protocol, it implements the protocol methods • Protocol methods can be mandatory or optional! • Using protocols (interfaces) you can define a contract that the implementor fulfills • If you have developed with Java: • protocol is an interface that may hold also optional methods!
  130. Example protocol Flyable { func fly() -> Void } class

    Animal {} class Bird : Animal, Flyable { func fly() { print("Bird flies") } } The first is inheritance, the rest are protocols
  131. Polymorphism protocol Flyable { func fly() -> Void } class

    Animal {} class Bird : Animal, Flyable { func fly() { print("Bird flies") } } class Airplane : Flyable { func fly() { print("Airplane flies") } } func fly(f : Flyable) { f.fly() } fly(f: Bird()) fly(f: Airplane()) Protocol type
  132. Properties • You can declare also a property to protocol

    • Get and Set • var width: Int { get set } • The property must be settable, cannot be let for example • Get • var width: Int { get } • The property can be settable!
  133. Properties protocol Shape { var width: Int { get set

    } } class Rectangle : Shape { } Does not conform to a protocol
  134. Properties protocol Shape { var width: Int { get set

    } } class Rectangle : Shape { var width = 0 } Now it does.. For Gettable & Settable Protocol Property, the requirement cannot be fulfilled by a constantly stored property or a read-only computed property.
  135. Properties protocol Shape { var width: Int { get set

    } } class Rectangle : Shape { let width = 0 } Contant variable, problem here For Gettable & Settable Protocol Property, the requirement cannot be fulfilled by a constantly stored property or a read-only computed property.
  136. Properties protocol Shape { var width: Int { get set

    } } class Rectangle : Shape { var _width = 0 var width: Int { get { return _width } set { if newValue > 0 { _width = width } } } } This works
  137. Properties protocol Shape { var width: Int { get set

    } } class Rectangle : Shape { var _width = 0 var width: Int { get { return _width } } } And now we have a problem...
  138. Properties protocol Shape { var width: Int { get }

    } class Rectangle : Shape { var width = 0 } Can also be settable!! Works..
  139. Delegation Pattern • The real power behind protocols comes when

    using polymorphism. • It’s possible to create pointer that points to whatever object as long as it conforms to protocol: • var x : Movable • You can define a message argument with the following argument type • func doSomething(x : Movable) • And now you can pass to this method whatever object as long as it’s conforming to this protocol!
  140. Delegation Pattern Example: GPS • CLLocationManager – class holds a

    following property • var delegate: CLLocationManagerDelegate • You can set the property • self.location = CLLocationManager() • self.location.delegate = ... • The delegate object can be what ever object as long as it’s conforming to CLLocationManagerDelegate • The CLLocationManagerDelegate holds several optional methods
  141. import Foundation import CoreLocation class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject {

    var location : CLLocationManager! @Published var done = false func fetch() { self.location = CLLocationManager() self.location.delegate = self self.location.requestWhenInUseAuthorization() self.location.startUpdatingLocation() print("start to fetch") } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { let latitude = locations[0].coordinate.latitude let longitude = locations[0].coordinate.longitude done = true } } The protocol One of protocol's optional methods
  142. Enums and Exception Handling

  143. Simple Enum Example enum Day { case monday case tuesday

    case wednesday } let day : Day = Day.monday print(day) Outputs "monday"
  144. Simple Enum Example enum Day { case monday case tuesday

    case wednesday } func output(day: Day) { print(day) } output(day: Day.monday) output(day: .monday) You can omit "Day." because type is known.
  145. Raw value enum Day : String { case monday =

    "Mon" case tuesday = "Tue" case wednesday = "Wed" } func output(day: Day) { print(day.rawValue) } output(day: Day.monday) output(day: .monday) "Mon"
  146. Raw value enum Day : Int { case monday =

    1 case tuesday case wednesday } func output(day: Day) { print(day.rawValue) } output(day: .monday) output(day: .tuesday) 1 2
  147. Error Handling with Enums enum MyError: Error { case maxOver

    case minUnder } func output(_ a: Int, _ b: Int) throws -> Int { let sum = a + b if sum > 100 { throw MyError.maxOver } else if sum < 0 { throw MyError.minUnder } return sum } do { let sum = try output(2,2) print(sum) } catch MyError.maxOver { print("Over max") } catch MyError.minUnder { print("Under min") }
  148. Error Handling with try? enum MyError: Error { case maxOver

    case minUnder } func output(_ a: Int, _ b: Int) throws -> Int { let sum = a + b if sum > 100 { throw MyError.maxOver } else if sum < 0 { throw MyError.minUnder } return sum } let sum = try? output(2,2) if let s = sum { print(s) } Returns now optional
  149. Opaque type

  150. Protocol - type protocol Flyable { func fly() -> Void

    } struct Bird: Flyable { func fly() -> Void { print("Bird") } } struct Airplane: Flyable { func fly() -> Void { print("Airplane") } } func giveFlyable() -> Flyable { return Bird() } let f = giveFlyable() f.fly() Returns protocol type
  151. Opaque - type protocol Flyable { func fly() -> Void

    } struct Bird: Flyable { func fly() -> Void { print("Bird") } } struct Airplane: Flyable { func fly() -> Void { print("Airplane") } } func giveFlyable() -> some Flyable { return Bird() } let f = giveFlyable() f.fly() Returns opaque type
  152. Protocol vs Opaque • Opaque types preserves type identity, protocol

    types do not • Opaque type always prefers to one specific concrete type • Protocol type can refer to many types as long as it is conforming to protocol
  153. Protocol type func giveFlyable() -> Flyable { return Bird() }

    var f = giveFlyable() f = Airplane() f.fly() f may contain Bird or Airplane
  154. Protocol type func giveFlyable() -> some Flyable { return Bird()

    } var f = giveFlyable() f = Airplane() f.fly() helloworld.swift:20:5: error: cannot assign value of type 'Airplane' to type 'some Flyable' f = Airplane()
  155. Protocol type func giveFlyable() -> some Flyable { return Bird()

    } var f = giveFlyable() f = Bird() f.fly() And it works now again...
  156. Protocols can have associated types protocol Flyable { associatedtype T

    var power : T { get } func fly() -> Void } The type can be determined later
  157. Adopting to Protocol struct Bird: Flyable { var power :

    Double func fly() -> Void { print("Bird flying with speed of \(power)") } } struct Airplane: Flyable { var power : Int func fly() -> Void { print("Airplane flying with speed of \(power)") } } func giveFlyable() -> Flyable { return Bird(power: 1.1) } var f = giveFlyable() f.fly() It is some object that conforms to the protocol. But what is the type of T? Swift cannot determinate this!
  158. Adopting to Protocol struct Bird: Flyable { var power :

    Double func fly() -> Void { print("Bird flying with speed of \(power)") } } struct Airplane: Flyable { var power : Int func fly() -> Void { print("Airplane flying with speed of \(power)") } } func giveFlyable() -> some Flyable { return Bird(power: 1.1) } var f = giveFlyable() f.fly() Opaque return type works... Will return always Bird with type of Double
  159. Recap • Protocol type can have any object as long

    as it's conforming to the protocol • This does not work with associated type, concrete type information is missing • use some keyword and let the implementation determinate the concrete type
  160. SwiftUI var body: some View { VStack() { Text("A") Text("B")

    } } VStack<TupleView<(Text, Text)>>
  161. Extensions

  162. Extensions • Extensions add new functionality to an existing class,

    structure, enumeration or protocol • Add properties • Define methods • Define inits • ...
  163. Example: String extension String { func isPalindrome() -> Bool {

    return self == String(self.reversed()) } } print("saippuakauppias".isPalindrome())
  164. How would you implement this? 3.repetitions { print("Hello!") } Int

    Add extension
  165. How would you implement this? 3.repetitions(task: { print("Hello") }) Without

    trailing closure
  166. How would you implement this? 3.repetitions { print("Hello!") } extension

    Int { func repetitions(task: () -> Void) { } } Argument is function
  167. How would you implement this? 3.repetitions { print("Hello!") } extension

    Int { func repetitions(task: () -> Void) { for _ in 0..<self { task() } } } And the implementation