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

Introducción a Swift para iOS

Introducción a Swift para iOS

Presentación original por Ana María Márquez y Javier de Silóniz, para el Betabeers de Julio 2015 en Cádiz.

Introducción a Swift para iOS, comentando las características básicas del lenguaje y algunas de las cosas chulas que podemos hacer con él.

Presentación en español, ejemplos de código en inglés.

More Decks by Javier de Silóniz Sandino

Other Decks in Programming

Transcript

  1. Orígenes Chris Lattner comienza a desarrollar Swi! en 2010. Autor

    de LLVM, que nace como su tesis doctoral. Autor de clang, el front-end de compilación usado en Xcode, en el SDK de PS4...
  2. Descripción "Swi! es Objective-C, sin C" — Chris Lattner ·

    Sintaxis más sencilla y concisa · Código más seguro · Conceptos claves de Objective-C, pero con una sintaxis moderna
  3. Código más conciso Una de las quejas respecto a Objective-C

    era su supuesta dificultad de aprendizaje. En los últimos años la cosa ha mejorado mucho (ej: ARC, modern syntax...), pero la sintaxis sigue siendo un engorro.
  4. Pasamos de esto: @interface AppModelClass : NSObject @property (nonatomic, copy)

    NSString *name; @property (nonatomic, copy) NSString *surname; @property (nonatomic, strong) NSArray *phoneNumbers; - (NSString *)description; - (instancetype)initWithName:(NSString *)name surname:(NSString *)surname phoneNumbers:(NSArray *)phoneNumbers; @end #import "AppModelClass.h" @implementation AppModelClass - (instancetype)initWithName:(NSString *)name surname:(NSString *)surname phoneNumbers:(NSArray *)phoneNumbers { if (self = [super init]) { self.name = name; self.surname = surname; self.phoneNumbers = phoneNumbers; } return self; } - (NSString *)description { NSString *phoneNumbersString = @""; for (NSString *phoneNumber in self.phoneNumbers) { phoneNumbersString = [phoneNumbersString stringByAppendingString:[NSString stringWithFormat:@"%@,", phoneNumber]]; } phoneNumbersString = [phoneNumbersString substringToIndex:[phoneNumbersString length] - 2]; return [NSString stringWithFormat:@"name: %@, surname: %@, phone numbers: %@", self.name, self.surname, phoneNumbersString]; } @end AppModelClass *model = [[AppModelClass alloc] initWithName:@"Javi" surname:@"Silóniz" phoneNumbers:@[@"+34645787878", @"+8167787679"]];
  5. A esto: class AppModelClass { let name: String let surname:

    String let phoneNumbers: [String] init(name: String, surname: String, phoneNumbers: [String]) { self.name = name self.surname = surname self.phoneNumbers = phoneNumbers } var description : String { get { let phoneString = phoneNumbers.reduce("", combine: { (result, number) -> String in return "\(result), \(number)" }) return "name: \(name), surname: \(surname), phones: \(phoneString)" } } } let model = AppModelClass(name: "Javi", surname: "Silóniz", phoneNumbers: ["+3467896828", "+8178288282"])
  6. Valores simples · La palabra reservada var nos permite crear

    variables: var coolNumber = 47 var coolCompany = "47 Degrees" Swi! inferirá los tipos de datos siempre que sea posible. Si no, le podemos ayudar nosotros: var coolNumber : Double = 47.0
  7. · También disponemos de let para crear constantes: let coolNumber

    = 47 let coolCompany = "47 Degrees" A una constante sólo se le puede asignar un valor una única vez.
  8. Valores simples Los tipos básicos en Swi! son objetos de

    la librería standard: · Tipos simples: Int, Double, Float, String... · Colecciones: Array, Dictionary...
  9. Valores simples Swi! es estrictamente tipado: una vez que asignamos

    un valor a una variable, no le podremos asignar otro de un tipo diferente. var notSoCoolNumbers = 11 notSoCoolNumbers = 666 // "47" is a String, and also is a cool number... // So this will fail at compile-time: notSoCoolNumbers = "47"
  10. Flujo de control Swi! soporta las clásicas estructuras de control

    heredadas de C: let notSoCoolNumbers = [4, 8, 15, 16, 23, 42] for number in notSoCoolNumbers { if number == 47 { println("Maybe this number is cool after all...") } } var numberWithCoolnessPotential = 0 while numberWithCoolnessPotential != 47 { numberWithCoolnessPotential++ } switch number { case 47: println("Radical!") default: println("Booh or Boo-arns!") }
  11. Funciones Para declarar funciones o métodos usamos la palabra reservada

    func: func functionName(paramName1: String, paramName2: Int, paramName3: [Int]) -> String { // actual code } // ... var stringResult = functionName("A string", 47, [4, 8, 15, 16, 23, 42])
  12. Clases y structs Para crear una clase usamos la palabra

    reservada class: class SomeClass { let property1 : String let property2 : String init(property1: String, property2: String) { // designated initializer code... } func someMethod(param1: Int) -> String { // code... } // Class method code... class func someClassMethod() { // ... } } // Instanciando y utilizando métodos: var instance = SomeClass(property1: "foo", property2: "bar") var someString = instance.someMethod(47)
  13. Clases y structs Podemos además crear estructuras con la palabra

    reservada struct: struct SomeStruct { let property1 : String let property2 : String init(property1: String, property2: String) { // designated initializer code... self.property1 = property1 self.property2 = property2 } func someMethod(param1: Int) -> String { // code... } } SomeStruct(property1: "foo", property2: "bar") Al usarlas en código, las clases se pasan por referencia y los structs por copia.
  14. Extensiones Equivalentes a las categorías en Objective-C, una extensión nos

    permite añadir funcionalidades extra a clases y structs ya existentes: extension String { func convertToUrlCode() -> String { // Do magic... } } let someString = "abc " let encodedString = someString.convertToUrlCode() // "abc%20"
  15. REPL y Playgrounds · REPL (Read Eval Print Loop): intérprete

    de Swi! en entorno de consola. Para acceder: · Comandos swift (Yosemite) o xcrun swift (Mavericks) · Comandos repl o expression en debugger LLDB.
  16. REPL y Playgrounds · Playgrounds: conjunto de scripts en Swi!

    que se auto-ejecutan al ser editados. · Herramienta perfecta para probar algoritmos o trozos pequeños de código. · En Swi! >1.2 permiten introducir imágenes, texto formateado... convirtiéndose en una herramienta perfecta para documentación.
  17. Propiedades computadas Conjunto de getter y setter que calculan un

    valor, en vez de almacenarlo: struct Rect { var origin = Point() var size = Size() var center: Point { get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: centerY) } set(newCenter) { origin.x = newCenter.x - (size.width / 2) origin.y = newCenter.y - (size.height / 2) } } } // We can use computed properties the same way we do with stored properties: let currentCenter = someRect.center someRect.center = CGPoint(10, 10)
  18. Lazy properties Una propiedad definida como lazy sólo se calculará

    la primera vez que accedamos a ella. class BigDataLoader { // This class will perform a lot of hard work once it's initialized... } class DataHandler { // ... so declaring it as a lazy value probably is a good idea. lazy var dataLoader = BigDataLoader() var fileNameToLoad // ... } let dataHandler = DataHandler() dataHandler.fileNameToLoad = "47DegreesStuff.sql" // ... some other operations, dataLoader is not instantiated yet... dataHandler.dataLoader.doImportantDiskOperation() // ... now that we accessed it, dataLoader gets created.
  19. Funciones como tipo de primer orden En Swi! podemos hacer

    con las funciones lo mismo que con cualquier otro dato: · Asignarlas a variables · Pasarlas como parámetros de otra función · Devolver funciones desde funciones
  20. Funciones como tipo de primer orden // Guardar una función

    (definida como closure) en una variable, y ejecutarla: var someFunction = { (param1: String) -> String in return param1.uppercaseString + "foo" } let result = someFunction("47 Degrees") // Generar funciones desde otra función: func generateMultiplier(factor: Int) -> (Int) -> Int { return { (x : Int) -> Int in x * factor } } let multiplier = generateMultiplier(2) let multiplyResult = multiplier(47) // Tomar una función como parámetro en otra función: func applyFunctionToNumbers(x: Int, y: Int, f: (Int, Int) -> Int) -> Int { return f(x, y) } let sumResult = applyFunctionToNumbers(46, 1, +)
  21. Funciones anidadas func convertMessageToH4X0RLanguage(originalPoliteMessage: String) -> String { func changeVocals()

    -> String { // Notice that we're accessing the outer scope from this closure! return String(map(originalPoliteMessage, { (character) -> Character in switch character { case "a", "A": return Character("4") case "e", "E": return Character("3") case "i", "I": return Character("1") case "o", "O": return Character("0") default: return character } })) } func changeUppercaseAndLowercaseLetters(source: String) -> String { var counter = 0 return String(map(source, { (character) -> Character in counter++ if counter % 2 == 0 { return Character(String(character).uppercaseString) } else { return Character(String(character).lowercaseString) } })) } return changeUppercaseAndLowercaseLetters(changeVocals()) } convertMessageToH4X0RLanguage("47 Degrees is the raddest company evah!") // Result: "47 D3Gr33S 1s tH3 r4dD3St c0mP4Ny 3V4H!"
  22. Tuplas Una tupla es simplemente un conjunto de valores, de

    igual o diferente tipo: var tuple1= ("Beta", "Beers") let tuple2 = (47, "Degrees") var tupleWithLotsOfThings = (4, 8.0, 15, "16", 23, 42) var tupleWithNames = (number: 1, letter: "a") // We can access the values from the tuples using the following syntax: let beta = tuple1.0 // "Beta" let number = tupleWithNames.number // 1 // Or we can deconstruct the tuple to variables directly like this: let (n1, n2, n3, n4, n5, n6) = tupleWithLotsOfThings
  23. Tuplas Un uso muy común de las tuplas es devolver

    varios valores desde una función: struct IntStack { // ... func pop() -> (Int, IntStack) { // In an immutable stack, it's nice to be able to // return the popped value and a copy of the new stack } } let stack = IntStack() // ...push stuff in stack... let (poppedValue, newStack) = stack.pop()
  24. Opcionales ¿Te suena esto? if (someDangerousValue != nil) { someDangerousValue.doSomethingEvenMoreDangerous();

    } else { someDangerousValue = [[SomeDangerousClass alloc] init]; } ¿Y esto? if (unknownUrlString != nil && [unknownUrlString isEqualToString:@""]) { NSURL *unknownUrl = [NSURL urlWithString:unknownUrlString]; launchUrlInSafari(unknownString); }
  25. Opcionales ¿Te suena esto? if (someDangerousValue != nil) { someDangerousValue.doSomethingEvenMoreDangerous();

    } else { someDangerousValue = [[SomeDangerousClass alloc] init]; } ¿Y esto? if (unknownUrlString != nil && [unknownUrlString isEqualToString:@""]) { NSURL *unknownUrl = [NSURL urlWithString:unknownUrlString]; launchUrlInSafari(unknownString); } ¡Eso no está bonito!
  26. Opcionales · Estamos acostumbrados a comprobar si un valor puede

    ser nulo... En Swi!, todas las variables y constantes deben contener un valor: let forbiddenConstant = nil // NO F***ING WAY · Si prevemos que una variable puede no contener un valor, usamos un tipo opcional: let possibleDangerousValue : String? = receiveDataFromTheNetwork()
  27. Opcionales Para tratar con el valor de un opcional podemos

    "desenvolverlo": if let correctValue1 = someOptionalValue { // ... do stuff } // From Swift 1.2 we can unwrap many optionals at the same time, // even if some of them depend on the others: if let correctValueA = someOptionalValue, let correctValueB = correctValueA.doSomethingDangerous() { // ... do stuff } // If one or more of the values come as nil, the code block won't get executed
  28. Opcionales También podemos encadenar operaciones sobre el opcional, usando el

    signo ? entre cada llamada: if let correctUrlString = someModelInstanceThatMayBeNil?.url?.absoluteString { // ... do stuff } Si cualquiera de las operaciones encadenadas devuelve nil, el resultado final será nil sin evaluar el resto.
  29. Pattern Matching Igual que en C y otros lenguajes, Swi!

    hace un uso intensivo de la estructura de control switch...
  30. Pattern Matching Igual que en C y otros lenguajes, Swi!

    hace un uso intensivo de la estructura de control switch... ...pero con ESTEROIDES.
  31. Pattern Matching Pattern matching nos permite comparar un valor con

    una serie de patrones, para tomar diferentes acciones en consecuencia: enum CardinalPoint { case North case South case West case East } let cardinalPoint = calculateCurrentCardinalPoint() switch cardinalPoint { case .North: println("We're heading North!") case .South: println("We're heading South!") case .West println("We're heading West!") case .East: println("We're heading East!") default: println("Where are we?!") }
  32. Pattern Matching Podemos ir más allá de lo que un

    bloque switch de C nos permite: enum CardinalPoint { case North(Double) case South(Double) case West(Double) case East(Double) } let cardinalPoint = calculateCurrentCardinalPoint() switch cardinalPoint { case .North(let degrees): println("We're heading \(degrees)º North!") case .South(let degrees): println("We're heading (degrees)º South!") case .West(let degrees) println("We're heading (degrees)º West!") case .East: println("We're heading East, but we don't care how many degrees for some reason!") default: println("Where are we?!")
  33. Los tipos de datos también constituyen patrones, algo útil cuando

    recibimos datos del tipo Any (no tipados): func checkIfInt(data: Any) -> Bool { switch data { case is Int: return true default: return false } } func castAsIntIfPossible(suspiciousNumber: Any) -> Int? { switch suspiciousNumber { case let x as Int: return x default: return nil } } checkIfInt("a") // false checkIfInt(2) // true castAsIntIfPossible("a") // nil castAsIntIfPossible(47) // 47
  34. Sobrecarga de operadores Swi! nos permite sobrecargar operadores existentes (+,

    -, *...) y usarlos con otros tipos datos (ya existentes o propios): func +(lhs: CGRect, rhs: CGRect) -> CGRect { var mostLeftRect = (lhs.origin.x < rhs.origin.x) ? lhs : rhs var mostUpperRect = (lhs.origin.y < rhs.origin.y) ? lhs : rhs return CGRectMake(mostLeftRect.origin.x, mostUpperRect.origin.y, lhs.size.width + rhs.size.width, lhs.size.height + rhs.size.height) } let frame1 = CGRectMake(0, 0, 30, 50) let frame2 = CGRectMake(20, 20, 80, 100) let sum = frame1 + frame2
  35. Operadores propios A su vez podemos crear nuestros propios operadores:

    // Here we define a new operator ">>" to push in a preexistent type IntStack: infix operator >> { associativity left precedence 140 } // Then we have to declare its implementation: func ++(lhs: IntStack, rhs: Int) -> IntStack { return lhs.push(hrs) }
  36. Operadores propios Los operadores pueden tener la siguiente estructura: ·

    Inline: (ej: x + y) · Postfix: (ej: x++) · Unary: (ej: -x)
  37. Swi" 2.0 y más allá Swi! es relativamente nuevo y

    actualmente está en continua evolución. El lenguaje está sometido a bastantes cambios en cada versión, creando algunos conflictos en código antiguo. Pero a la vez obtenemos jugosas novedades a menudo.
  38. Swi" 2.0 y más allá Swi! 2.0 (disponible en la

    versión beta de Xcode 7) nos promete: · Control de errores mejorado (bloques try-catch- finally) do { try doStuff() print("Data downloaded!") } catch MyError.NetworkError { print("Network error detected") } catch { print("Normal error detected") }
  39. Swi" 2.0 y más allá · Aplicación de Pattern Matching

    fuera de bloques switch (en for-in e if). for case .North(let degrees) in arrayOfCardinalPoints { println("I'm heading \(degrees)0 North!") }
  40. Swi" 2.0 y más allá · La palabra clave guard

    nos permite realizar early returns de una manera limpia: func sendRegistrationData(account: [String: String]) -> NSError? { guard let name = account["name"] as String, let surname = account["surname"] as String { return NSError(domain: kInvalidUserError, code: 666, userInfo: nil) } registerUser(name, surname) }
  41. Swift 2.0 y más allá: Second Bridge Las características de

    Swi! (soporte de inmutabilidad, pattern matching, funciones de primer orden...) permite que nuestro código sea: · Más limpio · Más seguro · Más funcional.
  42. Swi! 2.0 y más allá: Second Bridge De hecho nuestra

    intención desde 47 Degrees es hacerlo aun más seguro y funcional... ...y para ello hemos desarrollado Second Bridge, para ampliar las características funcionales del lenguaje.
  43. Swi! 2.0 y más allá · Second Bridge es un

    framework completamente open-source · Libre y gratuíto para proyectos comerciales o personales · Puedes (¡y debes!) colaborar para mejorarlo Pásate por http://47deg.github.io/second-bridge/ para echarle un vistazo ;).