Swift Karol Kubicki karol@tooploox.com Tooploox
Dzisiaj Podstawowe elementy języka wykorzystywane w codziennym programowaniu Przykłady w Playgrounds Kolejne spotkania Tworzenie funcjonalnej aplikacji
Źródło wiedzy The Swift Programming Language - dokumentacja Apple
Specyfika języka Głównie język obiektowy Można pisać imperatywnie Elementy funkcyjne - ale nie jest językiem funkcyjnym! Nowy język - premiera 2 VI 2014 Tak, mam 5 lat doświadczenia w Swift Karol Kubicki
Zmienne, stałe, inferencja typów var age: Int = 25 let name: String = "Karol" let pi: Double = 3.1415 inferencja typów var age = 25 let name = "Karol" let pi = 3.1415 różnica age = 30 // 30 name = "Jan" // compiler error pi = 2.7182 // compiler error
Podstawowe typy String Int, UInt Float, Double [String] = Array<String> [String : Int] = Dictionary<String, Int> Set<Double> (tuple1, tuple2)
Działania na String var name = "Karol" var surname = "Kubicki" Konkatenacja var fullname = name + " " + surname // "Karol Kubicki" Interpolacja var fullname = "\(name) \(surname)" // "Karol Kubicki"
Enum bez typu enum TurnSignal { case Left case Right z określonym typem enum Grade: Int { case One = 1, Two, Three, Four, Five, Six
Enum Przypisana wartość enum TurnSignal { case Left(UIColor) case Right(UIColor) Użycie let signal = TurnSignal.Left(UIColor.redColor()) switch turn { case.left(let color): (...) case.right(let color): (...)
Optional? Optional<T>
Optional Składnia var number: Int? number = nil // ok var age: Int = 10 var age = number // error - nie zgadza się typ Optional<Int> oraz Int var name = "Karol" name = nil // compiler error
Optional - c.d. enum Optional<Value> { case.some(value) case.none Określa czy zmienna ma wartośc czy jest nil func stringtoint(string: String) -> Int? Nie możemy odczytać wartości póki nie upewnimy się że istnieje Nigdy więcej null pointer exception
Optional - unwrapping var userinput: String? (...) if let input = userinput { // input istnieje print(input) else { // userinput jest nil
Optional - unwrapping Składnia if let jest skrótem do poniższego: var number: Int? (...) switch number { case.none: print("no value") case.some(let value): print(value)
Optional -! Force unwrap var number: Int? (...) print(number!) UWAGA - jeśli number jest równy nil, program się wywali Używać w ostateczności!
Kolekcje ["uwr", "pwr"] ["ios" : "Swift"]
Array var numbers = [Int]() // Array<Int>() numbers = [1, 2, 3, 4] numbers.isempty // false numbers.append(5) // [1, 2, 3, 4, 5] numbers.insert(0, atindex: 0) // [0,1,2,3,4,5] numbers.removeatindex(0) // [1, 2, 3, 4, 5] numers.first! // 1 numbers.indexof(5) // 4
Dictionary var grades = [String : Int]() grades = Dictionary<String, Int>() grades["tomek"] = 3 var tomekgrade: Int? = grades["tomek"] tomekgrade // 3 grades["pawel"] = 5 grades.keys // ["Pawel", "Tomek"] grades.values // [3, 4]
Set var players = Set<String>() players.insert("tom") // {"Tom" players.insert("matt") // {"Tom", "Matt" players.insert("tom") // {"Tom", "Matt" Unikalne elementy Nie uporządkowana Operacja na zbiorach Suma, różnica etc.
Control flow for index in 0...5 { // 0,1,2,3,4,5 for index in 0..<5 { // 0,1,2,3,4 for var index = 0; index < 5; ++index { // 0,1,2,3,4
Control flow - collections var numbers = [5,4,3,2,1] for number in numbers { // 5, 4, 3, 2, 1 for number in numbers where number > 3 { // 5, 4 var grades = ["Tom" : 5, "Frank" : 2] for (key, value) in grades { // do stuff
Control flow - while var index = 0 while index < 10 { // (...) repeat { // (...) while index < 10
Control flow - if if something { // else if somethingelse { // else { //
Control flow - switch enum Number { case Big, Small var value = Number.Big switch value { case.big: // case.small: // default nie jest konieczny
Control flow - switch switch number { case 0...10: // Small number case 11..<20: // Bigger number case 20..<100 where number % 2 == 0: // parzyste miedzy <20, 100) default: // Huge number default jest konieczny aby obsłużyć wszystkie możliwości
Switch - pattern matching case 0...10: // Range case numbers is Int: // Casting case (_, 10): // Tuples case (let x, _): // Przypisane wartości case let labels as UILabel: // Casting Pattern matching Apple Docs
Guard Wczesne wycofanie if w drugą stroną - zawsze z elsem func crossthebridge(colour: String) { guard color == "blue" else { // return error // Seek the holy grail "Możesz przejść jeśli spełnisz warunek"
Guard Sprawdzanie czy wartość istnieje func parseuserinput(input: String?) { // input typu Optional<String> guard let input = input else { // return error // inputy typu String
Guard - dlaczego? Sprawdzanie czy wartość istnieje func parseuserinput(input: String?) { guard let input = input else { // return error (...) func parseuserinput(input: String?) { if let input = input { (...)
Funkcje func addnumber(a: Int, b: Int) -> Int { return a + b func printmessage(message: String) { print(message) func highfive() -> Int { return 5
Funkcje func minmax(numbers: [Int]) -> (min: Int, max: Int) { (...) return (min: 1, max: 5) func hellomessage(message: String = "Hello World") { print(message) hellomessage() // "Hello World" hellomessage("hej") // Hej
Funkcje Nazywane parametry func sayhello(to person: String, otherperson: String) -> Int { return "Hello \(person) and \(otherperson)" sayhello(to: "Karol", otherperson: "Michal") func sayhello(to person: String, and otherperson: String) -> Int { return "Hello \(person) and \(otherperson)" sayhello(to: "Karol", and: "Michal")
Closures Funkcje anonimowe Domknięcia Lambdy
Closures Funkcja bez nazwy (anonimowa) Trzyma referencje użytych wewnątrz zmiennych (domyka) Zazwyczaj przekazywane w kodzie jako parametr Można przypisać do zmiennych
Closures var addnumbers: (a: Int, b: Int) -> Int addnumbers = { (a, b) in return a + b addnumbers(a: 10, b: 20) // 30
Closures - przykłady var numbers = [1,2,3,4,5] numbers.filter({ (number) -> Bool in return number > 3 ) // [4,5] numbers.map({ (number) -> Int in return number * 10 ) // [10,20,30,40,50]
Closures - przykłady var numbers = [3,2,4,1,5] numbers.sort({ (n1, n2) -> Bool in return n1 > n2 ) // [5,4,3,2,1] var multiplebytwo: (Int) -> Int multiplebytwo = { a in return a * 2 numbers.map(multiplebytwo) // [6,4,8,2,10]
Closures - skróty var numbers = [3,2,4,1,5] numbers.sort({ (n1: Int, n2: Int) -> Bool in return n1 > n2 ) // Trailing closure numbers.sort { (n1: Int, n2: Int) -> Bool in return n1 > n2 // Inferencje typów numbers.sort { (n1, n2) in return n1 > n2
Closures - skróty // Ostatnie wyrażenie domyślnie zwraca // Nawiasy parametrów numbers.sort { n1, n2 in n1 > n2 // $0, $1, etc - kolejne argumenty numbers.sort { $0 > $1 // Bo operator też jest funkcją :) numbers.sort(>)
Class Struct
Class and Struct Właściwości Metody Konstruktory - init Możne je rozszerzać - extension Mogą implementować - protocol
Class Reference type Może dziedziczyć Destruktor - deinit Struct Value type Nie może dziedziczyć
Class - przykład class User { var firstname: String var lastname: String init(firstname: String, lastname: String) { self.firstname = firstname self.lastname = lastname // Tworzenie obiektu User(firstName: "Karol", lastname: "Kubicki")
Struct - przykład struct Size { let width: Float let height: Float // Konstruktor tworzony automatycznie Struct(width: 100.0, height: 200.0)
Properties - właściwości class Address { var street = "Main Street" { didset { print("changed street") willset { print("will change \(newvalue)") lazy var streetnumber = 30
Properties - właściwości struct Square { var side: Double var perimeter: Double { get { return side * 4 set { side = newvalue / 4.0
Metody Funkcje w: - Klasach - Strukturach - Enumeratorach Rodzaje: - Metody obiekty - Metody typu
Metody struct Square { var side: Double func area() -> Double { return side * side static func sidescount() -> Int { return 4 var square = Suqare(side: 10) square.area() // 100 Square.sidesCount() // 4
Dziedziczenie tylko dla class class Vehicle { var currentspeed = 0.0 func speedup() { func stop() { currentspeed = 0.0 class Car: Vehicle { override func speedup() { currentspeed += 10.0
Dziedziczenie final class Motorbike: Vehicle { override func speedup() { currentspeed += 30.0 // Błąd kompilator class BigMotorbike: Motorbike { // Nie możemy dziedziczyć po `final` Dotyczy również metod final func dostuff() { (...)
Optional Chaining class Person { var address: Address? class Address { var apartmentnumber: Int? var person: Person? if let number = person?.address?.apartmentnumber { // we have number
Obsługa błędów enum NetworkError: ErrorType { case HttpError(errorCode: Int) case NoInternetConnection func getdata() throws -> String { if nointernet { throw NetworkError.NoInternetConnection else if httpcode!= 200 { throw NetworkError.HttpError(errorCode: httpcode) return somedata
Obsługa błędów - c.d. do { var downloadeddata = getdata() catch NetworkError.NoInternetConnection { // handle error catch NetworkError.HttpError(let httpcode) { // handle error // albo if let data = try? getdata() { // handel data else { // handle error
Type check func checkifint(object: AnyObject) { return object is Int if let number = object as? Int { // jest Int // NIEBEZPIECZNE let number = object as! Int! Lepiej nie korzystać z AnyObject (dla klas) i Any (dla wszystkiego) - poprawne typy są dobre!
Extension extension String { func toint() -> Int? { return Int(self) var doublestring: String { return self + self "10".toInt() // 10 "10".doubleString / "1010"
Extension protocol Printable { func printversion() -> String extension Int: Printable { func printversion() -> String { return "\(self)" 10.printVersion() // "10"
Protocols protocol Polygon { var sidescount: Int { get mutating func changesideslength(lengths: [Double]) func area() -> Double init(sideslengths: [Double]) Definuje interfejs komunikacji (metody, właściwości, etc) dla class, struct i enum - bez implementacji
Protocols class Square: Polygon { private var sideslengths: [Double] var sidescount: Int { return sideslengths.count func changesideslength(lengths: [Double]) { sideslengths = lengths func area() -> Double { let side = sideslengths.first! return side * side required init(sideslengths: [Double]) { self.sideslengths = sideslengths
Protocols struct RectangularTriangle: Polygon { private var sideslengths: [Double] var sidescount: Int { return sideslengths.count mutating func changesideslength(lengths: [Double]) { sideslengths = lengths func area() -> Double { let c = sideslengths.maxelement()! let sideandheight = sideslengths.filter { $0!= c return sideandheight[0] * sideandheight[1] * 0.5 init(sideslengths: [Double]) { self.sideslengths = sideslengths
Protocols Używane jako typ let square = Square(sidesLengths: [4,4,4,4]) let triangle = RectangularTriangle(sidesLengths: [4,5,6]) var polygons: [Polygon] = [square, triangle] // var polygons = [square, triangle] zadziała let totalarea = polygons.reduce(0.0) { sum, p in return sum + p.area()
Protocol Extensions protocol RandomNumberGenerator { func randomnumber() -> Int extension RandomNumberGenerator { func randomnumber() -> Int { return 5 // losowany rzut kością
Protocol Extensions Możemy określić dla jakiego typu domyślna implementacja istnieje protocol Printable { func printready() -> String extension CollectionType where Generator.Element: Printable { func printready() -> String { let itemsastext = self.map { $0.printReady() return "[" + itemsastext.joinwithseparator(", ") + "]"
Generics Określanie typy w trakcie kompilacji Korzystają z tego np. kolekcje np Array<Element> var intarray = Array<Int>() func dosomething<t>(a: T, b: T) { (...) dosomething(10, 10) // T jest Int
Generic - problem struct IntStack { var items = [Int]() mutating func push(item: Int) { items.append(item) mutating func pop() -> Int { return items.removelast()
Generic - rozwiązanie struct Stack<Element> { var items = [Element]() mutating func push(item: Element) { items.append(item) mutating func pop() -> Element { return items.removelast()
Generic - rozwiązanie var stackofstrings = Stack<String>() stackofstrings.push("uno") stackofstrings.push("dos") var stackofints = Stack<Int>() stackofstrings.push(10) stackofstrings.push(20)
Generic - ograniczenie typu Dictionary<Key: Hashable, Value> func count<t: Class, U: Protocol>(someT: T, someu: U) func find<t: Equatable>(element: T, array: [T]) -> T? Mówimy, że typ generyczny musi implementować protokół, lub musi dziedziczyć po klasie
Generic - protocols protocol SensorData { typealias RawData var rawdata: RawData { get set class MotionData: SensorData { typealias RawData = String var rawdata: RawData = ""
Access Levels public private internal
Dziękuję za uwagę
Q&A
Karol Kubicki karol@tooploox.com Tooploox