--- contributors: - ["Grant Timmerman", "http://github.com/grant"] - ["Christopher Bess", "http://github.com/cbess"] - ["Joey Huang", "http://github.com/kamidox"] - ["Anthony Nguyen", "http://github.com/anthonyn60"] translators: - ["David Hsieh", "http://github.com/deivuh"] filename: learnswift-es.swift --- Swift es un lenguaje de programación para el desarrollo en iOS y macOS creado por Apple. Diseñado para coexistir con Objective-C y ser más resistente contra el código erroneo, Swift fue introducido en el 2014 en el WWDC, la conferencia de desarrolladores de Apple. Véase también la guía oficial de Apple, [getting started guide](https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/), el cual tiene un completo tutorial de Swift. ```swift // Importar un módulo import UIKit // // MARK: Básicos // // XCode soporta referencias para anotar tu código y agregarlos a lista de la // barra de saltos. // MARK: Marca de sección // TODO: Hacer algo pronto // FIXME: Arreglar este código // En Swift 2, println y print fueron combinados en un solo método print. // Print añade una nueva línea automáticamente. print("Hola, mundo") // println ahora es print print("Hola, mundo", appendNewLine: false) // print sin agregar nueva línea // Valores de variables (var) pueden cambiar después de ser asignados // Valores de constrantes (let) no pueden cambiarse después de ser asignados var myVariable = 42 let øπΩ = "value" // nombres de variable unicode let π = 3.1415926 let convenience = "keyword" // nombre de variable contextual // Las declaraciones pueden ser separadas por punto y coma (;) let weak = "keyword"; let override = "another keyword" // Los acentos abiertos (``) permiten utilizar palabras clave como nombres de // variable let `class` = "keyword" let explicitDouble: Double = 70 let intValue = 0007 // 7 let largeIntValue = 77_000 // 77000 let label = "some text " + String(myVariable) // Conversión (casting) let piText = "Pi = \(π), Pi 2 = \(π * 2)" // Interpolación de string // Valores específicos de la compilación (build) // utiliza la configuración -D #if false print("No impreso") let buildValue = 3 #else let buildValue = 7 #endif print("Build value: \(buildValue)") // Build value: 7 /* Las opcionales son un aspecto del lenguaje Swift que permite el almacenamiento de un valor `Some` (algo) o `None` (nada). Debido a que Swift requiere que cada propiedad tenga un valor, hasta un valor 'nil' debe de ser explicitamente almacenado como un valor opcional. Optional es un enum. */ var someOptionalString: String? = "opcional" // Puede ser nil // Al igual que lo anterior, pero ? es un operador postfix (sufijo) var someOptionalString2: Optional = "opcional" if someOptionalString != nil { // No soy nil if someOptionalString!.hasPrefix("opt") { print("Tiene el prefijo") } let empty = someOptionalString?.isEmpty } someOptionalString = nil // Opcional implícitamente desenvuelto var unwrappedString: String! = "Un valor esperado." // Al igual que lo anterior, pero ! es un operador postfix (sufijo) var unwrappedString2: ImplicitlyUnwrappedOptional = "Un valor esperado." if let someOptionalStringConstant = someOptionalString { // tiene valor `Some` (algo), no nil if !someOptionalStringConstant.hasPrefix("ok") { // No tiene el prefijo } } // Swift tiene soporte de almacenamiento para cualquier tipo de valor. // AnyObject == id // A diferencia de Objective-C `id`, AnyObject funciona con cualquier // valor (Class, Int, struct, etc) var anyObjectVar: AnyObject = 7 anyObjectVar = "Cambiado a un valor string, no es buena práctica, pero posible." /* Comentar aquí /* Comentarios anidados también son soportados */ */ // // MARK: Colecciones // /* Tipos Array (arreglo) y Dictionary (diccionario) son structs (estructuras). Así que `let` y `var` también indican si son mudables (var) o inmutables (let) durante la declaración de sus tipos. */ // Array (arreglo) var shoppingList = ["catfish", "water", "lemons"] shoppingList[1] = "bottle of water" let emptyArray = [String]() // let == inmutable let emptyArray2 = Array() // igual que lo anterior var emptyMutableArray = [String]() // var == mudable // Dictionary (diccionario) var occupations = [ "Malcolm": "Captain", "kaylee": "Mechanic" ] occupations["Jayne"] = "Public Relations" let emptyDictionary = [String: Float]() // let == inmutable let emptyDictionary2 = Dictionary() // igual que lo anterior var emptyMutableDictionary = [String: Float]() // var == mudable // // MARK: Flujo de control // // Ciclo for (array) let myArray = [1, 1, 2, 3, 5] for value in myArray { if value == 1 { print("Uno!") } else { print("No es uno!") } } // Ciclo for (dictionary) var dict = ["uno": 1, "dos": 2] for (key, value) in dict { print("\(key): \(value)") } // Ciclo for (range) for i in -1...shoppingList.count { print(i) } shoppingList[1...2] = ["steak", "peacons"] // Utilizar ..< para excluir el último valor // Ciclo while var i = 1 while i < 1000 { i *= 2 } // Ciclo do-while do { print("Hola") } while 1 == 2 // Switch // Muy potente, se puede pensar como declaraciones `if` con _azúcar sintáctico_ // Soportan String, instancias de objetos, y primitivos (Int, Double, etc) let vegetable = "red pepper" switch vegetable { case "celery": let vegetableComment = "Add some raisins and make ants on a log." case "cucumber", "watercress": let vegetableComment = "That would make a good tea sandwich." case let localScopeValue where localScopeValue.hasSuffix("pepper"): let vegetableComment = "Is it a spicy \(localScopeValue)?" default: // obligatorio (se debe cumplir con todos los posibles valores de entrada) let vegetableComment = "Everything tastes good in soup." } // // MARK: Funciones // // Funciones son un tipo de primera-clase, quiere decir que pueden ser anidados // en funciones y pueden ser pasados como parámetros // Función en documentación de cabeceras Swift (formato reStructedText) /** Una operación de saludo - Una viñeta en la documentación - Otra viñeta en la documentación :param: name Un nombre :param: day Un día :returns: Un string que contiene el valor de name y day */ func greet(name: String, day: String) -> String { return "Hola \(name), hoy es \(day)." } greet("Bob", "Martes") // Similar a lo anterior, a excepción del compartamiento de los parámetros // de la función func greet2(requiredName: String, externalParamName localParamName: String) -> String { return "Hola \(requiredName), hoy es el día \(localParamName)" } greet2(requiredName:"John", externalParamName: "Domingo") // Función que devuelve múltiples valores en una tupla func getGasPrices() -> (Double, Double, Double) { return (3.59, 3.69, 3.79) } let pricesTuple = getGasPrices() let price = pricesTuple.2 // 3.79 // Ignorar tupla (u otros) valores utilizando _ (guión bajo) let (_, price1, _) = pricesTuple // price1 == 3.69 print(price1 == pricesTuple.1) // true print("Gas price: \(price)") // Cantidad variable de argumentos func setup(numbers: Int...) { // Es un arreglo let number = numbers[0] let argCount = numbers.count } // Pasando y devolviendo funciones func makeIncrementer() -> (Int -> Int) { func addOne(number: Int) -> Int { return 1 + number } return addOne } var increment = makeIncrementer() increment(7) // Pasando como referencia func swapTwoInts(inout a: Int, inout b: Int) { let tempA = a a = b b = tempA } var someIntA = 7 var someIntB = 3 swapTwoInts(&someIntA, &someIntB) print(someIntB) // 7 // // MARK: Closures (Clausuras) // var numbers = [1, 2, 6] // Las funciones son un caso especial de closure ({}) // Ejemplo de closure. // `->` Separa los argumentos del tipo de retorno // `in` Separa la cabecera del cuerpo del closure numbers.map({ (number: Int) -> Int in let result = 3 * number return result }) // Cuando se conoce el tipo, como en lo anterior, se puede hacer esto numbers = numbers.map({ number in 3 * number }) // o esto //numbers = numbers.map({ $0 * 3 }) print(numbers) // [3, 6, 18] // Closure restante numbers = sorted(numbers) { $0 > $1 } print(numbers) // [18, 6, 3] // Bastante corto, debido a que el operador < infiere los tipos numbers = sorted(numbers, < ) print(numbers) // [3, 6, 18] // // MARK: Estructuras // // Las estructuras y las clases tienen capacidades similares struct NamesTable { let names = [String]() // Subscript personalizado subscript(index: Int) -> String { return names[index] } } // Las estructuras tienen un inicializador designado autogenerado (implícitamente) let namesTable = NamesTable(names: ["Me", "Them"]) let name = namesTable[1] print("Name is \(name)") // Name is Them // // MARK: Clases // // Las clases, las estructuras y sus miembros tienen tres niveles de control de acceso // Éstos son: internal (predeterminado), public, private public class Shape { public func getArea() -> Int { return 0; } } // Todos los métodos y las propiedades de una clase son public (públicas) // Si solo necesitas almacenar datos en un objecto estructurado, // debes de utilizar `struct` internal class Rect: Shape { var sideLength: Int = 1 // Getter y setter personalizado private var perimeter: Int { get { return 4 * sideLength } set { // `newValue` es una variable implícita disponible para los setters sideLength = newValue / 4 } } // Lazily loading (inicialización bajo demanda) a una propiedad // subShape queda como nil (sin inicializar) hasta que getter es llamado lazy var subShape = Rect(sideLength: 4) // Si no necesitas un getter y setter personalizado // pero aún quieres ejecutar código antes y después de hacer get o set // a una propiedad, puedes utilizar `willSet` y `didSet` var identifier: String = "defaultID" { // El argumento `willSet` será el nombre de variable para el nuevo valor willSet(someIdentifier) { print(someIdentifier) } } init(sideLength: Int) { self.sideLength = sideLength // Siempre poner super.init de último al momento de inicializar propiedades // personalizadas super.init() } func shrink() { if sideLength > 0 { sideLength -= 1 } } override func getArea() -> Int { return sideLength * sideLength } } // Una clase simple `Square` que extiende de `Rect` class Square: Rect { convenience init() { self.init(sideLength: 5) } } var mySquare = Square() print(mySquare.getArea()) // 25 mySquare.shrink() print(mySquare.sideLength) // 4 // Conversión de tipo de instancia let aShape = mySquare as Shape // Comparar instancias, no es igual a == que compara objetos (equal to) if mySquare === mySquare { print("Yep, it's mySquare") } // Inicialización (init) opcional class Circle: Shape { var radius: Int override func getArea() -> Int { return 3 * radius * radius } // Un signo de interrogación como sufijo después de `init` es un init opcional // que puede devolver nil init?(radius: Int) { self.radius = radius super.init() if radius <= 0 { return nil } } } var myCircle = Circle(radius: 1) print(myCircle?.getArea()) // Optional(3) print(myCircle!.getArea()) // 3 var myEmptyCircle = Circle(radius: -1) print(myEmptyCircle?.getArea()) // "nil" if let circle = myEmptyCircle { // no será ejecutado debido a que myEmptyCircle es nil print("circle is not nil") } // // MARK: Enums // // Los enums pueden ser opcionalmente de un tipo específico o de su propio tipo // Al igual que las clases, pueden contener métodos enum Suit { case spades, hearts, diamonds, clubs func getIcon() -> String { switch self { case .spades: return "♤" case .hearts: return "♡" case .diamonds: return "♢" case .clubs: return "♧" } } } // Los valores de enum permite la sintaxis corta, sin necesidad de poner // el tipo del enum cuando la variable es declarada de manera explícita var suitValue: Suit = .hearts // Enums de tipo no-entero requiere asignaciones de valores crudas directas enum BookName: String { case john = "John" case luke = "Luke" } print("Name: \(BookName.john.rawValue)") // Enum con valores asociados enum Furniture { // Asociación con Int case desk(height: Int) // Asociación con String e Int case chair(String, Int) func description() -> String { switch self { case .desk(let height): return "Desk with \(height) cm" case .chair(let brand, let height): return "Chair of \(brand) with \(height) cm" } } } var desk: Furniture = .desk(height: 80) print(desk.description()) // "Desk with 80 cm" var chair = Furniture.chair("Foo", 40) print(chair.description()) // "Chair of Foo with 40 cm" // // MARK: Protocolos // // `protocol` puede requerir que los tipos tengan propiedades // de instancia específicas, métodos de instancia, métodos de tipo, // operadores, y subscripts protocol ShapeGenerator { var enabled: Bool { get set } func buildShape() -> Shape } // Protocolos declarados con @objc permiten funciones opcionales, // que te permite evaluar conformidad @objc protocol TransformShape { optional func reshaped() optional func canReshape() -> Bool } class MyShape: Rect { var delegate: TransformShape? func grow() { sideLength += 2 // Pon un signo de interrogación después de la propiedad opcional, // método, o subscript para ignorar un valor nil y devolver nil // en lugar de tirar un error de tiempo de ejecución // ("optional chaining") if let allow = self.delegate?.canReshape?() { // test for delegate then for method self.delegate?.reshaped?() } } } // // MARK: Otros // // `extension`: Agrega funcionalidades a tipos existentes // Square ahora se "conforma" al protocolo `Printable` extension Square: Printable { var description: String { return "Area: \(self.getArea()) - ID: \(self.identifier)" } } print("Square: \(mySquare)") // También puedes hacer extend a tipos prefabricados (built-in) extension Int { var customProperty: String { return "This is \(self)" } func multiplyBy(num: Int) -> Int { return num * self } } print(7.customProperty) // "This is 7" print(14.multiplyBy(3)) // 42 // Generics: Similar Java y C#. Utiliza la palabra clave `where` para // especificar los requerimientos de los genéricos. func findIndex(array: [T], valueToFind: T) -> Int? { for (index, value) in enumerate(array) { if value == valueToFind { return index } } return nil } let foundAtIndex = findIndex([1, 2, 3, 4], 3) print(foundAtIndex == 2) // true // Operadores: // Operadores personalizados puede empezar con los siguientes caracteres: // / = - + * % < > ! & | ^ . ~ // o // Caracteres unicode: math, symbol, arrow, dingbat, y line/box. prefix operator !!! {} // Un operador prefix que triplica la longitud del lado cuando es utilizado prefix func !!! (inout shape: Square) -> Square { shape.sideLength *= 3 return shape } // Valor actual print(mySquare.sideLength) // 4 // Cambiar la longitud del lado utilizando el operador !!!, // incrementa el tamaño por 3 !!!mySquare print(mySquare.sideLength) // 12 ```