Introducción

Swift es un nuevo lenguaje de programación para crear aplicaciones para iOS y OS X, basado en C y Objective-C, pero sin las restricciones de compatibilidad de C. Swift adopta patrones de programación seguros y añade funciones modernas para hacer que la programación sea más simple, flexible y divertida. La interfaz se basa en los marcos de trabajo Cocoa y Cocoa Touch, ampliamente apreciados, mostrando una nueva dirección en el desarrollo de software.

apple-swift

Swift ha existido durante años. Apple se basa en compiladores, depuradores y marcos de trabajo existentes como su infraestructura base. Simplifica la gestión de memoria mediante ARC (Automatic Reference Counting, recuento automático de referencias). Nuestra pila de marcos de trabajo siempre se ha basado en Cocoa. Objective-C ha evolucionado para admitir bloques, literales de colección y módulos, lo que permite que los marcos de trabajo de lenguajes modernos se utilicen sin profundizar. Gracias a este trabajo de base, es posible introducir un nuevo lenguaje de programación en el desarrollo de software de Apple.

Los desarrolladores de Objective-C sentirán una sensación de familiaridad con Swift. Swift adopta los parámetros con nombre y el modelo de objetos dinámico de Objective-C. Proporciona interoperabilidad con los marcos de trabajo de Cocoa y un enfoque 'mix-and-match'. Sobre estas bases, Swift introduce muchas características nuevas y combina funcionalidades de programación procedimental y orientada a objetos.

Swift también es amigable para los nuevos programadores. Es un lenguaje de programación de sistemas de calidad industrial, pero tan amigable como un lenguaje de scripts. Admite playgrounds, que permiten a los programadores experimentar con una parte de código de Swift y ver el resultado inmediatamente, sin tener que compilar y ejecutar una aplicación.

Swift integra las ideas de los lenguajes de programación modernos y la sabiduría de la cultura de ingeniería de Apple. El compilador está optimizado para el rendimiento, mientras que el lenguaje está optimizado para el desarrollo, sin necesidad de compromisos mutuos. Puedes empezar aprendiendo con 'Hello, world' y pasar a sistemas completos. Todo esto hace de Swift una fuente de innovación para los desarrolladores de software de Apple.

Swift es una forma fantástica de escribir aplicaciones para iOS y OS X, y seguirá impulsando la introducción de nuevas funciones. Estamos ansiosos por ver qué puedes hacer con él.

Iniciación a Swift

El aprendizaje de un nuevo lenguaje debería comenzar con imprimir 'Hello, world'. En Swift, es una sola línea:

println("Hello, world")

Si has escrito código en C u Objective-C, esta sintaxis te resultará familiar. En Swift, esto es un programa completo. No necesitas importar una biblioteca separada para la entrada/salida y el manejo de cadenas. El código a nivel global es el punto de entrada del programa, por lo que no necesitas escribir una función main(). Tampoco necesitas escribir punto y coma después de cada sentencia. Esta introducción te dará información suficiente para enseñarte a completar una tarea de programación. No te preocupes si todavía no entiendes algunas cosas; todo lo que no se explique con claridad se detallará más adelante en este libro.

Nota:Como buena práctica, puedes abrir este capítulo en un playground de Xcode. Los playgrounds te permiten editar código y ver los resultados de inmediato.

Valores simples

Usa let para definir constantes y var para definir variables. El valor de una constante no necesita especificarse en tiempo de compilación, pero debe asignarse al menos una vez. Esto significa que puedes usar una constante para nombrar un valor que determinas una sola vez, pero que usas en múltiples lugares.

var myVariable = 42
myVariable = 50
let myConstant = 42

Nota:La definición de constantes aquí es similar a las variables en lenguajes de programación funcional: una vez asignadas, no pueden modificarse. Úsalas con frecuencia; es beneficioso.

Una constante o variable debe tener el mismo tipo que el valor que se le asigna. Por lo tanto, no necesitas especificar estrictamente el tipo. Al proporcionar un valor, puedes crear una constante o variable y dejar que el compilador infiera su tipo. En el ejemplo anterior, el compilador infiere que myVariable es un tipo entero, porque su valor inicial es un entero.

Nota:El tipo está vinculado al nombre de la variable, lo que lo convierte en un lenguaje de tipado estático. Esto ayuda a la optimización estática. Se diferencia de Python, JavaScript, etc.

let imlicitInteger = 70
let imlicitDouble = 70.0
let explicitDouble: Double = 70

Los valores nunca se convierten implícitamente a otros tipos. Si necesitas convertir un valor a un tipo diferente, debes construir explícitamente una instancia del tipo deseado.

let label = "The width is "
let width = 94
let widthLabel = label + String(width)

Hay una forma más sencilla de incluir valores en cadenas: escribe el valor entre paréntesis y coloca una barra invertida (\) antes de los paréntesis. Por ejemplo:

let apples = 3
let oranges = 5     
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."

Para crear un arreglo y un diccionario se usan corchetes '[]', y para acceder a sus elementos se usa el índice o la clave entre corchetes.

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
   "Malcolm": "Captain",
   "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

Para crear un arreglo o diccionario vacío, usa la sintaxis de inicialización:

let emptyArray = String[]()
let emptyDictionary = Dictionary<String, Float>()

Si no se puede inferir la información de tipo, puedes escribir el arreglo vacío como '[]' y el diccionario vacío como '[:]', por ejemplo, cuando declaras una variable con un tipo conocido y la pasas como argumento a una función.

shoppingList = []   //去购物并买些东西

Flujo de control

Usa if y switch como control de condiciones. Usa for-in, for, while, do-while como bucles. Los paréntesis no son obligatorios, pero las llaves del cuerpo son obligatorias.

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScores += 3
    } else {
        teamScores += 1
    }
}
teamScore

En una sentencia if, la condición debe ser una expresión booleana, lo que significa que if score { ... } es incorrecto; no se puede comparar implícitamente con 0. Puedes usar if y let juntos para trabajar con valores que podrían no existir. Estos valores son opcionales. Un valor opcional puede contener un valor o nil para indicar que el valor aún no existe. Escribe un signo de interrogación '?' después del tipo para indicar que el valor es opcional.

var optionalString: String? = "Hello"
optionalString == nil

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}

Nota:Si el valor opcional es nil, la condición es false y el código entre llaves se omite. De lo contrario, el valor opcional se desempaqueta y se asigna a una constante, lo que hace que el valor desempaquetado esté disponible dentro del bloque de código.

switch admite múltiples tipos de datos y múltiples comparaciones, no se limita a enteros y a pruebas de igualdad.

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 x where x.hasSuffix("pepper"):
    let vegetableComment = "Is it a spicy \(x)?"
default:    
    let vegetableComment = "Everything tastes good in soup."
}

Nota:

Después de ejecutar el caso coincidente, el programa sale del switch en lugar de continuar con el siguiente caso. Por lo tanto, ya no se necesita break para salir del switch.

Puedes usar for-in para iterar sobre cada elemento de un diccionario, proporcionando un par de nombres para usar cada par clave-valor.

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}

Usa while para repetir un bloque de código hasta que la condición cambie. La condición del bucle puede colocarse al final para asegurar que el bucle se ejecute al menos una vez.

var n = 2
while n < 100 {
    n = n * 2
}
n

var m = 2
do {
    m = m * 2
} while m < 100
m

Puedes mantener un índice en el bucle usando '..' para indicar un rango de índices o declarando explícitamente un valor inicial, una condición y un incremento. Estos dos bucles hacen lo mismo:

var firstForLoop = 0
for i in 0..3 {
    firstForLoop += i
}
firstForLoop

var secondForLoop = 0
for var i = 0; i < 3; ++i {
    secondForLoop += 1
}
secondForLoop

Usa '..' para construir un rango que ignora el valor más alto, y '...' para construir un rango que incluye ambos valores.

Funciones y cierres

Usa func para declarar una función. Para llamarla, usa su nombre seguido de la lista de argumentos entre paréntesis. Usa -> para separar los nombres de los parámetros del tipo de retorno.

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")

Usa tuplas para devolver múltiples valores.

func getGasPrices() -> (Double, Double, Double) {
    return (3.59, 3.69, 3.79)
}
getGasPrices()

Las funciones pueden aceptar un número variable de argumentos, que se recogen en un arreglo.

func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}
sumOf()
sumOf(42, 597, 12)

Las funciones pueden anidarse. Una función anidada puede acceder a las variables de la función donde se define. Puedes usar funciones anidadas para organizar el código y evitar que sea demasiado largo y complejo.

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}   
returnFifteen()

Las funciones son de primera clase. Esto significa que una función puede devolver otra función.

func makeIncrementer() -> (Int -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

Una función puede aceptar otras funciones como argumentos.

func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}

func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)

Las funciones son en realidad un caso especial de cierres. Puedes escribir un cierre sin nombre, simplemente colocándolo entre llaves. Usa in para separar los parámetros y el valor de retorno del cuerpo.

numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
    })

Al escribir cierres hay varias opciones. Cuando el tipo de un cierre es conocido, por ejemplo como callback, puedes omitir sus parámetros y valor de retorno, o ambos. Un cierre de una sola sentencia puede devolver el valor directamente.

numbers.map({number in 3 * number})

Puedes referirte a un parámetro por número en lugar de por nombre, lo cual es útil para cierres muy cortos. Un cierre pasa su último argumento a la función como valor de retorno.

sort([1, 5, 3, 12, 2]) { $0 > $1 }

Objetos y clases

Usa class para crear una clase. Una propiedad se declara dentro de la clase como una constante o variable, solo que en el contexto de la clase. Los métodos y las funciones se escriben de la misma manera.

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

Crea una instancia de la clase colocando paréntesis después del nombre de la clase. Usa la sintaxis de punto para acceder a las propiedades y métodos de la instancia.

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

A esta versión de la clase Shape le falta algo importante: un inicializador para configurar la clase al crear una instancia. Usa init para crear uno.

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }   

    func simpleDescription() -> String {
        return "A Shape with \(numberOfSides) sides."
    }
}

Nota:self se usa para distinguir entre la propiedad name y el parámetro name. La declaración de un inicializador es igual que la de una función, excepto que crea una instancia de la clase. Cada propiedad necesita un valor, ya sea en su declaración o en el inicializador.

Usa deinit para crear un deinicializador, que realiza tareas de limpieza cuando el objeto se destruye.

Una subclase incluye el nombre de su superclase, separado por dos puntos. No es necesario declarar una clase base estándar al heredar, por lo que puedes omitir la superclase.

Los métodos de una subclase pueden sobrescribir la implementación de la superclase marcándolos con override. Sin override, el compilador lo considera un error. El compilador también comprueba aquellos métodos que no han sido sobrescritos.

class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }

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

    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}

let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

Nota:Las propiedades pueden tener getter y setter.

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
    get {
        return 3.0 * sideLength
    }
    set {
        sideLength = newValue / 3.0
    }
    }

    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length \(sideLength)."
    }
}

var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength

En el setter de perimeter, el nombre del nuevo valor es newValue. Puedes proporcionar un nombre que no entre en conflicto después de set.

Nota:El inicializador de EquilateralTriangle tiene 3 pasos diferentes:

  • Establecer el valor de las propiedades
  • Llamar al inicializador de la superclase
  • Aquí también se puede cambiar el valor de una propiedad definida por la superclase, y añadir trabajo adicional a métodos, getters y setters.

Si no necesitas una propiedad calculada, pero aún así quieres proporcionar trabajo que se ejecute después de establecer un valor, usa willSet y didSet. Por ejemplo, la siguiente clase garantiza que la longitud del lado de su triángulo sea igual a la longitud del lado de su rectángulo.

class TriangleAndSquare {
    var triangle: EquilaterTriangle {
    willSet {
        square.sideLength = newValue.sideLength
    }
    }

    var square: Square {
    willSet {
        triangle.sideLength = newValue.sideLength
    }
    }

    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilaterTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength

Los métodos de una clase tienen una diferencia importante con respecto a las funciones. Los nombres de los parámetros de una función solo se usan dentro de la función, pero los nombres de los parámetros de un método también se pueden usar al llamar al método (excepto el primer parámetro). Por defecto, un método tiene un parámetro con el mismo nombre, que es el que se usa en la llamada. Puedes especificar un segundo nombre para usarlo dentro del método.

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes times: Int) {
        count += amount * times
    }
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)

Cuando trabajas con valores opcionales, puedes escribir "?" antes de un operador, similar a métodos y propiedades. Si el valor antes de "?" ya es nil, todo lo que esté después de "?" se ignora automáticamente y toda la expresión es nil. De lo contrario, el valor opcional se desempaqueta, y todo lo que está después de "?" actúa sobre el valor desempaquetado. En ambos casos, el valor de toda la expresión es un valor opcional.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

Enumeraciones y estructuras

Usa enum para crear una enumeración. Al igual que las clases y otros tipos con nombre, las enumeraciones pueden tener métodos.

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescrition() -> String {
        switch self {
        case .Ace:
            return "ace"
        case .Jack:
            return "jack"
        case .Queen:
            return "queen"
        case .King:
            return "king"
        default:
            return String(self.toRaw())
        }
    }
}
let ace = Rank.Ace  
let aceRawValue = ace.toRaw()

En el ejemplo anterior, el tipo de los valores raw es Int, por lo que solo puedes especificar el primer valor raw. Los valores raw posteriores se asignan en orden. También puedes usar cadenas o números de punto flotante como valores raw de una enumeración.

Usa las funciones toRaw y fromRaw para convertir entre valores raw y valores de enumeración.

if let convertedRank = Rank.fromRaw(3) {
    let threeDescription = convertedRank.simpleDescription()
}

Los valores de los miembros de la enumeración son los valores reales, no los valores raw escritos de otra manera. De hecho, en algunos casos no hay valor raw, que es cuando no lo proporcionas.

enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "dismonds"
        case .Clubs:
            return "clubs"
        }
    }
}
let hearts = Suit.Hearts    
let heartsDescription = hearts.simpleDescription()

Observa las dos formas de referirse al miembro Hearts arriba: cuando se asigna a la constante hearts, el miembro de la enumeración Suit.Hearts se referencia por su nombre completo, porque la constante no tiene un tipo explícito. En el switch, el miembro se referencia como .Hearts, porque el valor de self ya se conoce. Puedes usar la forma abreviada cuando te resulte conveniente.

Usa struct para crear una estructura. Las estructuras admiten muchos de los mismos comportamientos que las clases, incluyendo métodos y constructores. Una diferencia importante es que al pasarse en el código, las estructuras siempre se copian (se pasan por valor), mientras que las clases se pasan por referencia.

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \
        (suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
<p><strong>注意:</strong>

Un miembro de instancia de una enumeración puede tener un valor asociado. Instancias del mismo miembro de enumeración pueden tener valores diferentes. Los asignas al crear la instancia. La diferencia entre los valores asociados y los valores raw: el valor raw de una enumeración es el mismo para todas sus instancias, y lo proporcionas al definir la enumeración.

Por ejemplo, supongamos que necesitas obtener las horas de salida y puesta del sol desde un servidor. El servidor puede responder con la información solicitada o con algún mensaje de error.

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure... \(error)"
}

Nota:Las horas de salida y puesta del sol se extraen realmente de ServerResponse mediante coincidencia de patrones parcial.

Referencias

Tutorial de introducción al lenguaje de programación Swift de Apple:http://gashero.iteye.com/blog/2075324

The Swift Programming Language:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/