cv.cm

Programación · Lenguajes de programación

Tutorial de Swift para principiantes

7 min de lectura

Respuesta corta

Swift es un lenguaje rápido, seguro y compilado creado por Apple para aplicaciones de iPhone, iPad, Mac, Apple Watch y Apple TV, y que también funciona en Linux y Windows. Guarda print("Hello, world") en hello.swift y ejecuta swift hello.swift. Las ideas centrales son las constantes con let y las variables con var, la inferencia de tipos, los opcionales para valores que pueden faltar, los structs y enums con semántica de valor, los protocolos y los errores tratados con do/try/catch.

Abrir Formateador JSON

Qué es Swift y para qué se usa

Swift fue presentado por Apple en 2014 como sustituto moderno de Objective-C y se publicó como código abierto en 2015; el proyecto está en swift.org. Es el lenguaje principal de las aplicaciones en plataformas Apple, normalmente con los frameworks SwiftUI o UIKit. Swift también se usa para código del lado del servidor (por ejemplo con el framework Vapor) y herramientas de línea de comandos. El lenguaje está diseñado para evitar errores comunes: las variables deben inicializarse, los arrays comprueban sus límites y nil debe tratarse de forma explícita.

Instalar Swift y ejecutar Hello, world

En un Mac, instala Xcode desde la App Store o las herramientas de línea de comandos de Xcode. En Linux y Windows, descarga una toolchain desde swift.org. Compruébalo con swift --version.

print("Hello, world")
swift hello.swift
# Hello, world
swiftc hello.swift -o hello   # compile to a binary
./hello

Para un proyecto más grande, ejecuta swift package init --type executable y después swift run. Los playgrounds de Xcode son otra forma fácil de experimentar.

Constantes, variables y tipos básicos

Usa let para constantes y var para variables. Prefiere let; el compilador avisa cuando un var no se modifica nunca. Los tipos se infieren, y Swift nunca convierte implícitamente entre tipos numéricos.

TipoEjemploNotas
Intlet n = 4264 bits en las plataformas modernas
Doublelet x = 3.14Tipo de coma flotante por defecto
Boollet ok = truetrue o false
Stringlet s = "hi \(name)"Texto Unicode, tipo de valor
Characterlet c: Character = "é"Un carácter tal como lo percibe el usuario
[Int], [String: Int][1, 2], ["a": 1]Array y diccionario
let name = "Ada"
var count = 7
count += 1
let avg = Double(count) / 2   // explicit conversion
print("\(name) has \(count) items, avg \(avg)")  // Ada has 8 items, avg 4.0
print(7 / 2, 7 % 2)          // 3 1

\(expression) dentro de un literal de cadena inserta un valor (interpolación de cadenas).

Control de flujo

let score = 72
if score >= 90 {
    print("A")
} else if score >= 70 {
    print("B")                 // B
} else {
    print("C")
}

for i in 0..<3 {
    print(i, terminator: " ")  // 0 1 2
}
print()

var n = 10
while n > 1 { n /= 2 }
print(n)                       // 1

let day = "SAT"
switch day {
case "SAT", "SUN":
    print("weekend")           // weekend
default:
    print("weekday")
}

switch debe ser exhaustivo, no continúa en la siguiente rama por defecto y puede hacer coincidir rangos (case 90...100:), tuplas y casos de enum.

Funciones y closures

Los parámetros tienen etiquetas de argumento que escribe quien llama, lo que hace que las llamadas se lean como frases. Usa _ para omitir una etiqueta. Los closures son funciones anónimas escritas entre llaves.

func greet(_ name: String, with greeting: String = "Hello") -> String {
    return "\(greeting), \(name)!"
}

print(greet("Lin"))                  // Hello, Lin!
print(greet("Lin", with: "Hi"))      // Hi, Lin!

let nums = [3, 1, 2, 5]
let squares = nums.map { $0 * $0 }
let evens = nums.filter { $0 % 2 == 0 }
let sorted = nums.sorted(by: >)
print(squares, evens, sorted)        // [9, 1, 4, 25] [2] [5, 3, 2, 1]

$0 se refiere al primer argumento del closure. Cuando un closure es el último argumento, puede escribirse después de los paréntesis (sintaxis de trailing closure).

Arrays, diccionarios y conjuntos

var fruits = ["apple", "pear"]
fruits.append("fig")
print(fruits.count, fruits[0], fruits.contains("fig"))  // 3 apple true

var ages: [String: Int] = ["ann": 31, "bob": 25]
ages["cy"] = 40
for (person, age) in ages.sorted(by: { $0.key < $1.key }) {
    print(person, age)                                  // ann 31, bob 25, cy 40
}
print(ages["zed", default: 0])                          // 0

let tags: Set = ["b", "a", "b"]
print(tags.count)                                       // 2

Las colecciones son tipos de valor: asignar un array a otra variable crea una copia independiente (copiada de forma diferida, solo cuando una de ellas cambia). Un array declarado con let no puede modificarse en absoluto.

Característica distintiva: los opcionales

Un valor que puede faltar tiene un tipo opcional, que se escribe String?. No puedes usarlo como String hasta que lo desenvuelves, así que el compilador te obliga a tratar el caso nil.

let input = "42"
let number: Int? = Int(input)      // nil if input is not a number

if let number {                     // unwrap: runs only when not nil
    print("got \(number)")          // got 42
}

func firstWord(_ text: String) -> String? {
    text.split(separator: " ").first.map(String.init)
}

guard let word = firstWord("hello world") else {
    fatalError("no words")
}
print(word)                                  // hello
print(firstWord("")?.uppercased() ?? "none") // none

if let y guard let desenvuelven de forma segura, ?. (encadenamiento opcional) llama a un método solo cuando el valor existe, y ?? proporciona un valor por defecto. El desenvolvimiento forzado value! falla cuando el valor es nil, así que úsalo poco. La forma corta if let number requiere Swift 5.7 o posterior.

Structs, clases, enums y protocolos

Los structs y enums son tipos de valor; las clases son tipos de referencia con herencia. El código Swift prefiere los structs y los protocolos (interfaces) a las jerarquías de clases.

protocol Shape {
    func area() -> Double
}

struct Rect: Shape {
    var width: Double
    var height: Double
    func area() -> Double { width * height }
}

enum Circle: Shape {
    case unit
    case radius(Double)
    func area() -> Double {
        switch self {
        case .unit: return Double.pi
        case .radius(let r): return Double.pi * r * r
        }
    }
}

let shapes: [any Shape] = [Rect(width: 2, height: 3), Circle.radius(1)]
let total = shapes.reduce(0) { $0 + $1.area() }
print((total * 100).rounded() / 100)   // 9.14

Los structs reciben automáticamente un inicializador memberwise (Rect(width:height:)). Los casos de un enum pueden llevar valores asociados, como hace .radius(Double).

Manejo de errores

Las funciones que pueden fallar se marcan con throws. Quien las llama debe usar try dentro de un bloque do/catch, o usar try? para convertir un error en nil.

enum AgeError: Error {
    case notANumber(String)
    case negative
}

func parseAge(_ text: String) throws -> Int {
    guard let age = Int(text) else { throw AgeError.notANumber(text) }
    guard age >= 0 else { throw AgeError.negative }
    return age
}

do {
    print(try parseAge("41"))          // 41
    print(try parseAge("abc"))
} catch AgeError.notANumber(let text) {
    print("not a number: \(text)")     // not a number: abc
} catch {
    print("other error: \(error)")
}

let maybe = try? parseAge("-3")        // nil
print(maybe as Any)                    // nil

Un programa completo pequeño: frecuencia de palabras

import Foundation

let args = CommandLine.arguments
guard args.count == 2 else {
    print("usage: swift words.swift FILE")
    exit(1)
}

do {
    let text = try String(contentsOfFile: args[1], encoding: .utf8).lowercased()
    let words = text.split { !$0.isLetter && $0 != "'" }
    var counts: [Substring: Int] = [:]
    for word in words {
        counts[word, default: 0] += 1
    }
    for (word, n) in counts.sorted(by: { $0.value > $1.value }).prefix(5) {
        print(String(n).padding(toLength: 6, withPad: " ", startingAt: 0) + word)
    }
} catch {
    print("cannot read \(args[1]): \(error.localizedDescription)")
    exit(1)
}

Ejecútalo con swift words.swift notes.txt. Foundation ofrece lectura de archivos y utilidades para cadenas; está disponible en las plataformas Apple y en las toolchains de Linux y Windows.

Errores comunes de principiantes

  • Intentar cambiar una constante let o una propiedad de un struct declarado con let. Usa var cuando el valor deba cambiar.
  • Esperar que un struct se comparta como un objeto. Cambiar una copia no cambia el original; usa una clase si necesitas identidad compartida.
  • Indexar un String con un entero, como s[0]. Usa s.first, s.prefix(3) o valores de String.Index.
  • Mezclar Int y Double en una operación aritmética sin convertir, lo que es un error de compilación.
  • Olvidar try antes de una llamada que lanza errores, o usar try!, que falla ante un error.

Adónde ir después

Lee «The Swift Programming Language» en docs.swift.org, la guía y referencia oficial, y el centro de documentación de swift.org. Para aplicaciones, sigue los tutoriales de SwiftUI de Apple en Xcode. Después aprende la concurrencia de Swift (async/await y actores), los genéricos y el Swift Package Manager para las dependencias.

Preguntas frecuentes

¿Swift es solo para plataformas Apple?

No. Swift es de código abierto y también funciona en Linux y Windows, y se usa para código del lado del servidor y herramientas. Sin embargo, los frameworks de aplicaciones como SwiftUI y UIKit son exclusivos de Apple.

¿Debo aprender Swift u Objective-C?

Aprende Swift. Es el lenguaje que Apple recomienda para las aplicaciones nuevas, y puede llamar a código Objective-C existente cuando haga falta.

¿Cuál es la diferencia entre un struct y una clase en Swift?

Un struct es un tipo de valor que se copia al asignarlo o pasarlo; una clase es un tipo de referencia, de modo que todas las variables comparten una sola instancia. Las clases también admiten herencia y deinicializadores.

¿Qué significa el signo de interrogación después de un tipo?

Int? es un opcional: contiene un Int o nil. Debes desenvolverlo, por ejemplo con if let, antes de usar el valor.

Todos los tutoriales de programación