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
• 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"
/ 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
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"
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
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)
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
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
{ 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())
? 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) }
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) }
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
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
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)") }
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)
• 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)")
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!
{ if(number > 0) { success("it was positive") } } isPositiveInteger(number: 5) { print($0) } If final argument is function, you can use also trailing syntax
println(item) } for item in 1...5 { println(item) } for _ in 1...5 { } • Enumerate for(index, value) in enumerate(shoppingList) { println(index) println(value) }
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
// 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
(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
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)
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)
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
"" 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
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
• 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
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
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
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
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
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
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.
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)
• 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) { ... }
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
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?
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!
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
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
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!
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!
• 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!
} } 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.
} } 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.
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!
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
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") }
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
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
} var f = giveFlyable() f = Airplane() f.fly() helloworld.swift:20:5: error: cannot assign value of type 'Airplane' to type 'some Flyable' f = Airplane()
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!
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
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