Upload
vuongtu
View
218
Download
0
Embed Size (px)
Citation preview
Dzisiaj
• Podstawowe elementy języka wykorzystywane w codziennym programowaniu
• Przykłady w Playgrounds
Kolejne spotkania
• Tworzenie funcjonalnej aplikacji
Źródło wiedzyThe 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 = 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): (...)}
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!
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 - 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")
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(>)
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 } }}
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 = ""}