swift - autonom.iiar.pwr.wroc.pl · specyfika języka •głównie język obiektowy • można...

73
Swift Karol Kubicki [email protected] Tooploox

Upload: vuongtu

Post on 01-Mar-2019

218 views

Category:

Documents


0 download

TRANSCRIPT

Dzisiaj

• Podstawowe elementy języka wykorzystywane w codziennym programowaniu

• Przykłady w Playgrounds

Kolejne spotkania

• Tworzenie funcjonalnej aplikacji

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 = 25let name: String = "Karol"let pi: Double = 3.1415

inferencja typówvar age = 25let name = "Karol"let pi = 3.1415

różnicaage = 30 // 30name = "Jan" // compiler errorpi = 2.7182 // compiler error

Podstawowe typyStringInt, UIntFloat, Double[String] = Array<String>[String : Int] = Dictionary<String, Int>Set<Double>(tuple1, tuple2)

Działania na Stringvar name = "Karol"var surname = "Kubicki"

Konkatenacjavar fullName = name + " " + surname // "Karol Kubicki"

Interpolacjavar fullName = "\(name) \(surname)" // "Karol Kubicki"

Enumbez typuenum TurnSignal { case Left case Right}

z określonym typemenum Grade: Int { case One = 1, Two, Three, Four, Five, Six}

EnumPrzypisana wartośćenum TurnSignal { case Left(UIColor) case Right(UIColor)}

Użycielet signal = TurnSignal.Left(UIColor.redColor())switch turn { case .Left(let color): (...) case .Right(let color): (...)}

Optional?

Optional<T>

OptionalSkładniavar number: Int?number = nil // ok

var age: Int = 10var 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 nilfunc stringToInt(string: String) -> Int?

• Nie możemy odczytać wartości póki nie upewnimy się że istnieje

• Nigdy więcej null pointer exception

Optional - unwrappingvar userInput: String?

(...)

if let input = userInput { // input istnieje print(input)} else { // userInput jest nil}

Optional - unwrappingSkł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"]

Arrayvar 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! // 1numbers.indexOf(5) // 4

Dictionaryvar grades = [String : Int]()grades = Dictionary<String, Int>()

grades["Tomek"] = 3var tomekGrade: Int? = grades["Tomek"]tomekGrade // 3

grades["Pawel"] = 5

grades.keys // ["Pawel", "Tomek"]grades.values // [3, 4]

Setvar 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 flowfor 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 - collectionsvar 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 - whilevar index = 0

while index < 10 { // (...)}

repeat { // (...)} while index < 10

Control flow - ifif someThing { //} else if someThingElse { //} else { //}

Control flow - switchenum Number { case Big, Small}

var value = Number.Big

switch value { case .Big: // case .Small: //}

default nie jest konieczny

Control flow - switchswitch 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 matchingcase 0...10: // Rangecase numbers is Int: // Castingcase (_, 10): // Tuplescase (let x, _): // Przypisane wartościcase let labels as UILabel: // Casting

Pattern matching Apple Docs

GuardWczesne 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"

GuardSprawdzanie 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ść istniejefunc parseUserInput(input: String?) { guard let input = input else { // return error } (...)}

func parseUserInput(input: String?) { if let input = input { (...) }}

Funkcjefunc addNumber(a: Int, b: Int) -> Int { return a + b}

func printMessage(message: String) { print(message)}

func highFive() -> Int { return 5}

Funkcjefunc 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

FunkcjeNazywane 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")

ClosuresFunkcje 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

Closuresvar addNumbers: (a: Int, b: Int) -> Int

addNumbers = { (a, b) in return a + b}

addNumbers(a: 10, b: 20) // 30

Closures - przykładyvar 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ładyvar numbers = [3,2,4,1,5]

numbers.sort({ (n1, n2) -> Bool in return n1 > n2})// [5,4,3,2,1]

var multipleByTwo: (Int) -> IntmultipleByTwo = { a in return a * 2}numbers.map(multipleByTwo)// [6,4,8,2,10]

Closures - skrótyvar numbers = [3,2,4,1,5]numbers.sort({ (n1: Int, n2: Int) -> Bool in return n1 > n2})

// Trailing closurenumbers.sort { (n1: Int, n2: Int) -> Bool in return n1 > n2}

// Inferencje typównumbers.sort { (n1, n2) in return n1 > n2}

Closures - skróty// Ostatnie wyrażenie domyślnie zwraca// Nawiasy parametrównumbers.sort { n1, n2 in n1 > n2}

// $0, $1, etc - kolejne argumentynumbers.sort { $0 > $1 }

// Bo operator też jest funkcją :)numbers.sort(>)

ClassStruct

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ładclass User { var firstName: String var lastName: String

init(firstName: String, lastName: String) { self.firstName = firstName self.lastName = lastName }}

// Tworzenie obiektuUser(firstName: "Karol", lastName: "Kubicki")

Struct - przykładstruct Size { let width: Float let height: Float}

// Konstruktor tworzony automatycznieStruct(width: 100.0, height: 200.0)

Properties - właściwościclass Address { var street = "Main Street" { didSet { print("changed street") } willSet { print("will change \(newValue)") } }

lazy var streetNumber = 30}

Properties - właściwościstruct Square { var side: Double

var perimeter: Double { get { return side * 4 } set { side = newValue / 4.0 } }}

MetodyFunkcje w:- Klasach- Strukturach- Enumeratorach

Rodzaje:- Metody obiekty- Metody typu

Metodystruct Square { var side: Double

func area() -> Double { return side * side }

static func sidesCount() -> Int { return 4 }}

var square = Suqare(side: 10)square.area() // 100Square.sidesCount() // 4

Dziedziczenietylko dla class

class Vehicle { var currentSpeed = 0.0 func speedUp() {} func stop() { currentSpeed = 0.0 } }

class Car: Vehicle { override func speedUp() { currentSpeed += 10.0 }}

Dziedziczeniefinal class Motorbike: Vehicle { override func speedUp() { currentSpeed += 30.0 }}

// Błąd kompilatorclass BigMotorbike: Motorbike { // Nie możemy dziedziczyć po `final`}

Dotyczy również metod final func doStuff() { (...) }

Optional Chainingclass 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ówenum 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 checkfunc checkIfInt(object: AnyObject) { return object is Int }

if let number = object as? Int { // jest Int}

// NIEBEZPIECZNElet number = object as! Int

! Lepiej nie korzystać z AnyObject (dla klas) i Any (dla wszystkiego) - poprawne typy są dobre !

Extensionextension String { func toInt() -> Int? { return Int(self) }

var doubleString: String { return self + self }}

"10".toInt() // 10"10".doubleString / "1010"

Extensionprotocol Printable { func printVersion() -> String}

extension Int: Printable { func printVersion() -> String { return "\(self)" }}

10.printVersion() // "10"

Protocolsprotocol 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

Protocolsclass 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 }}

Protocolsstruct 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 }}

ProtocolsUż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 Extensionsprotocol RandomNumberGenerator { func randomNumber() -> Int}

extension RandomNumberGenerator { func randomNumber() -> Int { return 5 // losowany rzut kością }}

Protocol ExtensionsMożemy określić dla jakiego typu domyślna implementacja istniejeprotocol 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 - problemstruct IntStack { var items = [Int]() mutating func push(item: Int) { items.append(item) } mutating func pop() -> Int { return items.removeLast() }}

Generic - rozwiązaniestruct Stack<Element> { var items = [Element]() mutating func push(item: Element) { items.append(item) } mutating func pop() -> Element { return items.removeLast() }}

Generic - rozwiązanievar 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 - protocolsprotocol SensorData { typealias RawData var rawData: RawData { get set }}

class MotionData: SensorData { typealias RawData = String var rawData: RawData = ""}

Access Levelspublic privateinternal

Dziękuję za uwagę

Q&A