DevTools

Cheatsheet Swift

Referência completa da linguagem Swift — segura, rápida e moderna para iOS/macOS

Voltar às linguagens
Swift
72 cards encontrados
Categorias:
Versões:

Básico


9 cards
Variáveis e Constantes
let nome = "Swift"       // constante (imutável)
var idade = 10           // variável (mutável)
let pi: Double = 3.14    // tipo explícito
var contador: Int = 0

idade = 11               // OK (var)
// nome = "Outro"        // ERRO (let)

// let é preferido por segurança:
// O compilador avisa se var nunca muda
// Permite otimizações agressivas

// Múltipla declaração:
let x = 1, y = 2, z = 3
var a, b, c: Int         // sem valor inicial

let cria constantes imutáveis — preferir sempre. var só quando o valor muda. Inferência de tipos elimina declarações redundantes. O compilador otimiza let e previne bugs de mutação acidental.

Conversões de tipos
let x = 42
let s = String(x)        // Int → String
let d = Double(x)        // Int → Double
let f = Float(3.14)      // Double → Float

// String → número (retorna Optional!):
let n = Int("123")       // Int? = 123
let inv = Int("abc")     // Int? = nil

// Não há conversões implícitas:
// let y: Double = x     // ERRO!
let y: Double = Double(x) // correto

// Bool → Int não existe:
// let b = Int(true)     // ERRO!
let b = true ? 1 : 0    // workaround

Swift proíbe conversões implícitas — evita bugs silenciosos. Int("abc") retorna nil (não crash). Sempre que converte String para número, o resultado é Optional. Usar Double(x) explicitamente. Não há conversão Bool→Int.

Ranges e strides
// Closed range (inclui fim):
for i in 1...5 { print(i) }  // 1 2 3 4 5

// Half-open range (exclui fim):
for i in 0..<5 { print(i) }  // 0 1 2 3 4

// Stride com passo:
for i in stride(from: 0, to: 10, by: 2) {
    print(i)  // 0 2 4 6 8
}

// Stride inclusivo:
for i in stride(from: 10, through: 0, by: -2) {
    print(i)  // 10 8 6 4 2 0
}

// Ranges como coleção:
let nums = Array(1...100)
let slice = nums[5..<10]

// Verificar pertença:
(1...10).contains(5)   // true
(1...10).contains(15)  // false

1...5 = fechado (inclui 5). 0..<5 = meio-aberto (exclui 5). stride(from:to:by:) para passos custom. through inclui o fim. Ranges funcionam como coleções. contains() verifica pertença em O(1) para ranges.

Tipos de dados
let inteiro: Int = 42
let decimal: Double = 3.14159
let flag: Bool = true
let letra: Character = "A"
let texto: String = "Swift"

// Tuplos:
let http: (Int, String) = (200, "OK")
print(http.0)  // 200
print(http.1)  // "OK"

// Tuplos com labels:
let ponto = (x: 3.0, y: 4.0)
print(ponto.x) // 3.0

// Tipos numéricos:
// Int, Int8, Int16, Int32, Int64
// UInt, UInt8, UInt16, UInt32, UInt64
// Float (32-bit), Double (64-bit)

Swift tem tipos estritos — sem conversões implícitas. Int e Double são os mais usados. Character = um Unicode grapheme cluster. Tuplos agrupam valores sem criar struct. Float = 32 bits, Double = 64 bits (preferir).

Operadores
// Aritméticos:
let soma = 5 + 3       // 8
let resto = 10 % 3     // 1
let div = 10 / 3       // 3 (Int division!)
let divD = 10.0 / 3.0  // 3.333...

// Comparação:
5 == 5   // true
5 != 3   // true
5 > 3    // true
5 <= 5   // true

// Lógicos:
true && false  // false (AND)
true || false  // true  (OR)
!true          // false (NOT)

// Range:
1...5    // 1,2,3,4,5 (fechado)
1..<5    // 1,2,3,4   (meio-aberto)

// Atribuição composta:
var x = 10
x += 5   // 15
x *= 2   // 30

Divisão inteira trunca: 10 / 3 = 3. Usar Double para decimais. ... é range fechado (inclui fim). ..< é meio-aberto (exclui fim). Operadores lógicos: &&, ||, !. Sem operador ternário em condições complexas — preferir if.

Optionals
var nome: String? = nil   // pode ser nil
nome = "Swift"

// Unwrapping seguro (if let):
if let n = nome {
    print(n)  // só executa se não-nil
}

// Optional chaining:
let tam = nome?.count     // Int? (nil se nome nil)

// Nil-coalescing:
let valor = nome ?? "default"

// Force unwrap (evitar!):
// let x = nome!  // crash se nil!

// Optional binding múltiplo:
if let a = nome, let b = nome?.uppercased() {
    print(a, b)
}

Optionals são o mecanismo central de segurança — forçam tratamento de ausência. if let faz unwrap seguro. ?. encadeia sem crash. ?? fornece fallback. ! (force unwrap) causa crash se nil — evitar sempre.

Type aliases e tuplos
typealias UserID = Int
typealias Callback = (String) -> Void
typealias Ponto = (x: Double, y: Double)

let id: UserID = 42
let p: Ponto = (x: 3.0, y: 4.0)
print(p.x)  // 3.0

// Tuplos como retorno:
func dividir(_ a: Int, _ b: Int) -> (quoc: Int, resto: Int) {
    return (a / b, a % b)
}
let r = dividir(17, 5)
print(r.quoc)   // 3
print(r.resto)  // 2

// Decomposição:
let (q, rest) = dividir(17, 5)

typealias cria nomes semânticos (sem custo runtime). Tuplos com labels permitem acesso por nome. Ideais para retornos simples sem criar structs. Decomposição com let (a, b) = tuplo. Não abusar — para 3+ campos, preferir struct.

Strings
let saudacao = "Olá"
let nome = "Mundo"

// Interpolação (preferido):
let msg = "Olá, \(nome)!"

// Concatenação:
let completa = saudacao + ", " + nome

// Multi-linha:
let multi = """
    Linha 1
    Linha 2
    """

// Métodos úteis:
msg.count              // 13
msg.uppercased()       // OLÁ, MUNDO!
msg.lowercased()       // olá, mundo!
msg.contains("Mundo")  // true
msg.hasPrefix("Olá")   // true
msg.split(separator: " ")  // ["Olá,", "Mundo!"]

String interpolation (\()) é a forma preferida. Triple-quoted (\"\"\") para multilinha. Strings são value types e Unicode-correct. count conta caracteres (não bytes). split retorna array de Substring.

Nil-coalescing e chaining
let nome: String? = nil
let apelido: String? = "Silva"

// Nil-coalescing (??):
let display = nome ?? "Anónimo"     // "Anónimo"

// Encadeamento:
let resultado = nome ?? apelido ?? "Sem nome"  // "Silva"

// Optional chaining:
let tam = nome?.count               // nil
let upper = nome?.uppercased()      // nil

// Chaining + coalescing:
let len = nome?.count ?? 0          // 0

// Multi-nível:
struct User { var address: Address? }
struct Address { var city: String? }
let city = user?.address?.city ?? "N/A"

?? fornece default quando optional é nil. Encadeamento cria fallbacks em cascata. ?. encadeia chamadas — retorna nil se qualquer nível for nil. Combinar ?. + ?? é o padrão idiomático. Substitui if-let verbose para casos simples.

Controlo de Fluxo


9 cards
If / Else
let temp = 25

if temp > 30 {
    print("Quente")
} else if temp > 20 {
    print("Agradável")
} else {
    print("Frio")
}

// Chaves OBRIGATÓRIAS (mesmo 1 linha)
// Parênteses opcionais na condição
// Condição DEVE ser Bool (não aceita Int)

// If como expressão (Swift 5.9+):
let msg = if temp > 25 { "quente" } else { "frio" }

// If-let (optional binding):
if let nome = optionalNome {
    print(nome)  // nome unwrapped aqui
}

Chaves {} são obrigatórias — previne bugs de indentação. Condição deve ser Bool (não aceita inteiros como C). if let faz unwrap de optionals. Swift 5.9+ permite if como expressão. Parênteses na condição são opcionais.

Guard e early exit
func processar(nome: String?, idade: Int?) {
    guard let n = nome else {
        print("Nome obrigatório")
        return  // DEVE sair do scope
    }
    guard let i = idade, i >= 18 else {
        print("Idade inválida")
        return
    }
    // Aqui n e i estão unwrapped:
    print("\(n) tem \(i) anos")
}

// Vantagem sobre if-let:
// - Happy path sem nesting
// - Variável disponível em todo o scope
// - Condição de falha clara no topo

// guard com throw:
func validar(_ x: Int) throws {
    guard x > 0 else { throw ValidationError.negativo }
}

guard verifica condições e sai cedo se falharem. O else DEVE sair do scope (return, throw, break). A variável unwrapped fica disponível no resto da função. Preferido sobre if-let quando precisa do valor em todo o scope.

Labeled statements
// Labels em loops:
outer: for i in 1...5 {
    inner: for j in 1...5 {
        if i * j > 12 {
            break outer  // sai do loop externo!
        }
        if j == 3 {
            continue inner  // próximo j
        }
        print("\(i)×\(j) = \(i*j)")
    }
}

// Label em switch dentro de loop:
loop: while true {
    switch estado {
    case .sair:
        break loop  // sai do while!
    case .continuar:
        continue loop
    default:
        break  // só sai do switch
    }
}

Labels (nome:) identificam loops para break/continue específicos. break outer sai do loop externo. Sem label, break dentro de switch só sai do switch. Essencial para loops aninhados e máquinas de estados.

Switch
let dia = 3
switch dia {
case 1:
    print("Segunda")
case 2, 3:
    print("Terça ou Quarta")
case 4...6:
    print("Meio da semana")
default:
    print("Fim de semana")
}

// DEVE ser exaustivo (ou ter default)
// NÃO faz fallthrough automático
// break é implícito (não necessário)

// fallthrough explícito (raro):
switch dia {
case 1:
    print("Início")
    fallthrough
case 2:
    print("Também")
default: break
}

switch deve ser exaustivo — cobrir todos os casos ou ter default. Não faz fallthrough automático (evita bugs de C). Suporta ranges (4...6), múltiplos valores (2, 3) e padrões complexos. break é implícito.

Pattern matching
let ponto = (x: 3, y: -2)
switch ponto {
case (0, 0):
    print("Origem")
case (_, 0):
    print("Eixo X")
case (0, _):
    print("Eixo Y")
case (-2...2, -2...2):
    print("Perto da origem")
default:
    print("Ponto (\(ponto.x), \(ponto.y))")
}

// if-case:
if case (let x, 0) = ponto {
    print("x = \(x)")
}

// for-case:
let dados: [Any] = [1, "dois", 3.0, "quatro"]
for case let s as String in dados {
    print(s)  // "dois", "quatro"
}

Pattern matching decompõe tuplos, verifica ranges e usa wildcards (_). if case extrai padrões fora de switch. for case let x as Type filtra por tipo. Poderoso para enums com associated values, parsing e validação de estados.

For-In
// Range:
for i in 1...5 {
    print(i)  // 1 2 3 4 5
}

// Ignorar valor:
for _ in 1...3 {
    print("Olá")  // 3x
}

// Coleção com índice:
let frutas = ["maçã", "banana", "cereja"]
for (i, fruta) in frutas.enumerated() {
    print("\(i): \(fruta)")
}

// Dicionário:
let caps = ["PT": "Lisboa", "BR": "Brasília"]
for (pais, cap) in caps {
    print("\(pais) → \(cap)")
}

// Filtro inline (where):
for i in 1...20 where i % 3 == 0 {
    print(i)  // 3 6 9 12 15 18
}

for-in itera ranges, arrays, dicts, sets. _ ignora o valor. enumerated() dá índice + valor. Dicionários retornam tuplos (key, value). where filtra inline. Não existe for (i=0; i — usar ranges.

Ternário e expressões
let idade = 20
let status = idade >= 18 ? "adulto" : "menor"

// Switch como expressão (Swift 5.9+):
let dia = 3
let nome = switch dia {
    case 1: "Segunda"
    case 2: "Terça"
    case 3: "Quarta"
    default: "Outro"
}

// If como expressão (Swift 5.9+):
let msg = if idade >= 18 {
    "Maior de idade"
} else {
    "Menor de idade"
}

// Encadeamento ternário (evitar!):
// let x = a ? b : c ? d : e  // ilegível

Operador ternário (?:) para condições simples. Swift 5.9+ permite switch e if como expressões (atribuir resultado). Evitar encadear ternários — ilegível. Preferir switch expressão para 3+ casos.

While e Repeat-While
// While (verifica antes):
var n = 5
while n > 0 {
    print(n)
    n -= 1
}

// Repeat-while (executa pelo menos 1x):
var input: String
repeat {
    input = readLine() ?? ""
    print("Recebido: \(input)")
} while input != "sair"

// Equivalente ao do-while de C/Java
// Útil para:
// - Validação de input
// - Menus interativos
// - Retry com condição

// Cuidado: garantir que termina!
// var x = 0
// while x < 10 { }  // loop infinito!

while verifica antes (pode nunca executar). repeat-while executa pelo menos uma vez (equivalente ao do-while). Ideal para validação de input e menus. Garantir que a condição eventualmente fica falsa — evitar loops infinitos.

Defer
func lerFicheiro() throws -> String {
    let file = try openFile("dados.txt")

    defer {
        file.close()  // SEMPRE executa
        print("Ficheiro fechado")
    }

    // Mesmo com throw ou return cedo:
    guard file.isValid else {
        throw FileError.invalido
        // defer executa aqui!
    }

    return try file.read()
    // defer executa aqui também!
}

// Múltiplos defer (ordem inversa):
defer { print("1º") }
defer { print("2º") }
// Output: "2º", "1º"

defer executa código ao sair do scope — mesmo com return, throw ou break. Ideal para cleanup: fechar ficheiros, libertar recursos, unlock. Múltiplos defer executam em ordem inversa (LIFO). Equivale a finally de Java/C#.

Funções e Closures


9 cards
Declaração de funções
func soma(_ a: Int, _ b: Int) -> Int {
    return a + b
}

// Com argument labels:
func greet(to nome: String, from remetente: String) -> String {
    return "Olá \(nome), de \(remetente)"
}
greet(to: "Ana", from: "Rui")

// Sem retorno:
func log(_ msg: String) {
    print("[LOG] \(msg)")
}

// Retorno implícito (Swift 5.1+):
func dobro(_ x: Int) -> Int { x * 2 }

// Múltiplos retornos (tuplo):
func stats(_ nums: [Int]) -> (min: Int, max: Int, avg: Double) {
    (nums.min()!, nums.max()!, Double(nums.reduce(0,+)) / Double(nums.count))
}

func declara funções. Labels externos tornam chamadas auto-documentadas. _ suprime o label. Retorno implícito em funções de uma expressão. Tuplos para múltiplos retornos. -> Void é omitido quando não há retorno.

Funções como tipos
// Tipo de função:
var operacao: (Int, Int) -> Int

operacao = { $0 + $1 }
print(operacao(3, 4))  // 7

operacao = { $0 * $1 }
print(operacao(3, 4))  // 12

// Função que retorna função:
func multiplicador(_ fator: Int) -> (Int) -> Int {
    return { $0 * fator }
}
let triplo = multiplicador(3)
triplo(5)  // 15

// Função como parâmetro:
func aplicar(_ f: (Int) -> Int, _ x: Int) -> Int {
    f(x)
}
aplicar({ $0 * 2 }, 5)  // 10

Funções são first-class citizens — armazenáveis, passáveis, retornáveis. O tipo (Int, Int) -> Int descreve a assinatura. Permite estratégias dinâmicas e composição. Base para callbacks, delegates e programação funcional.

Funções genéricas
func trocar<T>(_ a: inout T, _ b: inout T) {
    (a, b) = (b, a)
}
var x = 1, y = 2
trocar(&x, &y)  // x=2, y=1

// Com constraints:
func maximo<T: Comparable>(_ a: T, _ b: T) -> T {
    a > b ? a : b
}
maximo(3, 7)      // 7
maximo("a", "z")  // "z"

// Where clause:
func primeiroPar<T: Collection>(_ c: T) -> T.Element?
    where T.Element: BinaryInteger {
    c.first { $0 % 2 == 0 }
}

// Múltiplos tipos:
func zip<A, B>(_ a: [A], _ b: [B]) -> [(A, B)] {
    Array(Swift.zip(a, b))
}

Funções genéricas funcionam com qualquer tipo mantendo type safety. <T> declara o tipo. Constraints (T: Comparable) restringem. where clause para condições complexas. O compilador especializa para cada tipo concreto (zero-cost).

Parâmetros especiais
// Default values:
func conectar(host: String, porta: Int = 3306) {
    print("\(host):\(porta)")
}
conectar(host: "localhost")        // porta = 3306
conectar(host: "db", porta: 5432)  // porta = 5432

// Variadic (...):
func media(_ nums: Double...) -> Double {
    nums.reduce(0, +) / Double(nums.count)
}
media(8, 9, 10)  // 9.0

// Inout (passagem por referência):
func trocar(_ a: inout Int, _ b: inout Int) {
    (a, b) = (b, a)
}
var x = 1, y = 2
trocar(&x, &y)  // x=2, y=1

Default values eliminam sobrecargas. ... (variadic) aceita N argumentos. inout passa por referência (modifica original) — requer & na chamada. Ordem: normais → default → variadic. Um variadic por função.

Escaping e Autoclosure
// @escaping: closure sobrevive à função
var callbacks: [() -> Void] = []
func registar(_ cb: @escaping () -> Void) {
    callbacks.append(cb)  // armazenado para depois
}

// @autoclosure: adia avaliação
func log(nivel: String, msg: @autoclosure () -> String) {
    if nivel == "DEBUG" {
        print(msg())  // só avalia se necessário
    }
}
log(nivel: "INFO", msg: "Dados: \(dadosCaros())")
// dadosCaros() NÃO executa se nivel != DEBUG

// Sem @escaping: closure é chamado DENTRO da função
func executar(_ cb: () -> Void) {
    cb()  // não precisa de @escaping
}

@escaping indica que o closure será chamado depois da função retornar (callbacks, async). @autoclosure envolve expressão num closure sem {} — permite avaliação preguiçosa. Sem @escaping, o closure é chamado dentro da função.

Closures
// Sintaxe completa:
let soma = { (a: Int, b: Int) -> Int in
    return a + b
}

// Inferência de tipos:
let dobro: (Int) -> Int = { $0 * 2 }

// Trailing closure:
let nums = [3, 1, 4, 1, 5]
let ordenado = nums.sorted { $0 < $1 }

// Shorthand extremo:
let asc = nums.sorted(by: <)

// Closure que captura variáveis:
var total = 0
let acumular = { (x: Int) in total += x }
acumular(5)
acumular(3)
print(total)  // 8

Closures são blocos auto-contidos (lambdas). $0, $1 são shorthand para parâmetros. Trailing closure simplifica quando é o último argumento. sorted(by: <) é o caso mais conciso. Closures capturam variáveis do scope envolvente por referência.

Throwing functions
enum ParseError: Error {
    case vazio
    case invalido(linha: Int)
}

func parse(_ input: String) throws -> [Int] {
    guard !input.isEmpty else { throw ParseError.vazio }
    return input.split(separator: ",").enumerated().map { i, s in
        guard let n = Int(s.trimmingCharacters(in: .whitespaces)) else {
            throw ParseError.invalido(linha: i + 1)
        }
        return n
    }
}

// Chamar:
do {
    let nums = try parse("1, 2, 3")
} catch ParseError.invalido(let linha) {
    print("Erro na linha \(linha)")
}

// try? (optional) e try! (force):
let nums = try? parse("1,2")  // [Int]?

throws na assinatura indica que pode lançar erros. throw lança. try chama. do-catch captura casos específicos. try? converte em optional (nil se erro). try! crasha se erro (evitar). Enums conformando Error definem domínios.

Higher-order functions
let nums = [1, 2, 3, 4, 5]

// map: transforma cada elemento
let dobros = nums.map { $0 * 2 }       // [2,4,6,8,10]

// filter: seleciona por condição
let pares = nums.filter { $0 % 2 == 0 } // [2,4]

// reduce: acumula num valor
let soma = nums.reduce(0) { $0 + $1 }   // 15
let soma2 = nums.reduce(0, +)           // 15

// compactMap: transforma + remove nils
let strs = ["1", "a", "3"].compactMap { Int($0) }
// [1, 3]

// forEach: itera sem retornar
nums.forEach { print($0) }

map/filter/reduce são a base funcional. compactMap descarta nils. forEach para side effects. Não mutam a coleção original (retornam nova). Encadeáveis para pipelines declarativos. Preferir sobre loops manuais.

Async functions
// Função assíncrona:
func fetchDados() async throws -> [String] {
    let (data, _) = try await URLSession.shared
        .data(from: url)
    return try JSONDecoder().decode([String].self, from: data)
}

// Chamar com Task:
Task {
    do {
        let dados = try await fetchDados()
        print(dados)
    } catch {
        print("Erro: \(error)")
    }
}

// Múltiplas chamadas em paralelo:
async let a = fetchA()
async let b = fetchB()
let resultados = try await [a, b]

// await suspende sem bloquear a thread

async marca funções assíncronas. await suspende sem bloquear a thread. Task {} cria contexto de execução. async let executa em paralelo. Substitui completion handlers com código sequencial legível. Requer Swift 5.5+.

Coleções


9 cards
Arrays
var nums = [1, 2, 3, 4, 5]
nums.append(6)
nums.insert(0, at: 0)
nums.remove(at: 0)
nums[0] = 10

print(nums.count)       // 5
print(nums.isEmpty)     // false
print(nums.first ?? 0)  // 10
print(nums.contains(3)) // true

// Operações:
nums += [7, 8]          // concatenar
let slice = nums[1...3] // ArraySlice
let reversed = nums.reversed()

// Tipados:
let nomes: [String] = ["Ana", "Rui"]
let matriz: [[Int]] = [[1,2], [3,4]]

Arrays são value types com copy-on-write. Tipados ([Int] ou Array<Int>). append/insert/remove para mutação. first/last retornam optionals. += concatena. Slices são views (não copiam).

Ordenação e pesquisa
let nums = [5, 2, 8, 1, 9]

let asc = nums.sorted()          // [1,2,5,8,9]
let desc = nums.sorted(by: >)    // [9,8,5,2,1]

// Custom comparator:
struct Pessoa { let nome: String; let idade: Int }
let pessoas = [Pessoa(nome: "Ana", idade: 25),
               Pessoa(nome: "Rui", idade: 30)]
let porIdade = pessoas.sorted { $0.idade < $1.idade }

// Pesquisa:
let primeiro = nums.first { $0 > 4 }     // 5
let indice = nums.firstIndex(of: 8)      // Optional(2)
let todos = nums.filter { $0 > 3 }       // [5,8,9]

// contains com condição:
nums.contains { $0 > 8 }  // true

sorted() retorna novo array (não muta). sorted(by:) aceita closure custom. first(where:) encontra 1º elemento. firstIndex(of:) retorna Optional. contains(where:) verifica existência. Tudo imutável por default.

Coleções custom (Sequence)
// Conformar Sequence:
struct Countdown: Sequence {
    let start: Int

    func makeIterator() -> CountdownIterator {
        CountdownIterator(current: start)
    }
}

struct CountdownIterator: IteratorProtocol {
    var current: Int
    mutating func next() -> Int? {
        guard current > 0 else { return nil }
        defer { current -= 1 }
        return current
    }
}

// Uso:
for n in Countdown(start: 5) {
    print(n)  // 5 4 3 2 1
}

// Ganha map, filter, reduce, etc.:
let pares = Countdown(start: 10).filter { $0 % 2 == 0 }

Conformar Sequence + IteratorProtocol cria coleções custom. next() retorna nil quando termina. Ganha todas as operações funcionais gratuitamente (map, filter, reduce). Ideal para geradores, streams e wrappers.

Dictionaries
var capitais = ["PT": "Lisboa", "BR": "Brasília"]

capitais["ES"] = "Madrid"      // adicionar
capitais["PT"] = "Porto"       // atualizar
let removido = capitais.removeValue(forKey: "BR")

// Acesso seguro (retorna Optional):
if let cap = capitais["PT"] {
    print(cap)  // "Porto"
}

// Default value:
let valor = capitais["XX", default: "N/A"]

// Iteração:
for (pais, cap) in capitais {
    print("\(pais) → \(cap)")
}

// Chaves e valores:
let paises = Array(capitais.keys)
let cidades = Array(capitais.values)

Dictionaries mapeiam chaves únicas para valores. Acesso por chave retorna Optional. default: evita unwrap. removeValue(forKey:) remove e retorna. Iteração retorna tuplos (key, value). Chaves devem ser Hashable.

flatMap e compactMap
// flatMap: achata coleções aninhadas
let listas = [[1,2], [3,4], [5]]
let plana = listas.flatMap { $0 }  // [1,2,3,4,5]

// compactMap: remove nils
let strings = ["1", "abc", "3", "4x"]
let nums = strings.compactMap { Int($0) }  // [1, 3]

// Combinados:
let resultado = listas
    .flatMap { $0 }
    .filter { $0 > 2 }  // [3,4,5]

// Caso real: parsing de JSON array
let ids = respostas.compactMap { $0["id"] as? Int }

// flatMap com optional:
let opt: [Int?] = [1, nil, 3, nil, 5]
let validos = opt.compactMap { $0 }  // [1,3,5]

flatMap transforma e achata coleções aninhadas. compactMap é map que descarta nils — essencial para parsing. Juntos eliminam loops aninhados e verificações manuais. compactMap { $0 } remove nils de array de optionals.

Sets
let primos: Set = [2, 3, 5, 7, 11]
let pares: Set = [2, 4, 6, 8, 10]

// Operações de conjuntos:
let uniao = primos.union(pares)
let intersecao = primos.intersection(pares)  // {2}
let diferenca = primos.subtracting(pares)    // {3,5,7,11}
let simetrica = primos.symmetricDifference(pares)

// Verificações:
primos.contains(5)              // true
primos.isSubset(of: uniao)      // true
primos.isDisjoint(with: pares)  // false

// Eliminar duplicados:
let nums = [1, 2, 2, 3, 3, 3]
let unicos = Array(Set(nums))   // [1, 2, 3]

// Inserção/remoção:
var s: Set<Int> = [1, 2]
s.insert(3)
s.remove(2)

Sets armazenam elementos únicos sem ordem. Ideais para verificar pertença (O(1)) e eliminar duplicados. Operações: union, intersection, subtracting, symmetricDifference. Requerem Hashable. Sem ordem garantida.

Lazy e performance
let nums = Array(1...1_000_000)

// Eager: cria arrays intermédios
let r1 = nums.filter { $0 % 2 == 0 }
    .map { $0 * 2 }
    .prefix(5)

// Lazy: processa só o necessário
let r2 = nums.lazy
    .filter { $0 % 2 == 0 }
    .map { $0 * 2 }
    .prefix(5)
let resultado = Array(r2)  // [2,4,6,8,10]

// Sem lazy: processa 1M elementos
// Com lazy: processa ~10 elementos

// Quando usar lazy:
// - Coleções grandes
// - Só precisa de prefix/suffix
// - Pipeline com filter + map

lazy adia computação até ser necessária — processa elemento a elemento. Crucial para coleções grandes com prefix. Sem lazy: cria arrays intermédios (memória). Com lazy: O(k) em vez de O(n). Converter com Array() no fim.

Operações funcionais
let nums = [1, 2, 3, 4, 5, 6]

let dobros = nums.map { $0 * 2 }
let pares = nums.filter { $0 % 2 == 0 }
let soma = nums.reduce(0, +)  // 21

// joined:
let texto = nums.map(String.init)
    .joined(separator: ", ")
// "1, 2, 3, 4, 5, 6"

// Encadeamento (pipeline):
let resultado = nums
    .filter { $0 > 2 }
    .map { $0 * 10 }
    .reduce(0, +)
// 30+40+50+60 = 180

// sorted + prefix:
let top3 = nums.sorted(by: >).prefix(3)  // [6,5,4]

map/filter/reduce para transformações declarativas. Encadeamento cria pipelines legíveis. joined converte para string. sorted(by:) + prefix para top-N. Retornam novos arrays (não mutam). Preferir sobre loops manuais.

Dictionary operations
var scores = ["Ana": 85, "Rui": 92, "Mia": 78]

// Atualizar com default:
scores["Ana", default: 0] += 5  // 90

// mapValues (mantém chaves):
let bonus = scores.mapValues { $0 + 10 }

// filter:
let aprovados = scores.filter { $0.value >= 80 }

// merge:
let extras = ["Zé": 70]
scores.merge(extras) { old, new in new }

// Agrupar (grouping):
let nums = [1, 2, 3, 4, 5, 6]
let grupos = Dictionary(grouping: nums) { $0 % 2 == 0 ? "par" : "impar" }
// ["par": [2,4,6], "impar": [1,3,5]]

// Compactar:
let keys = ["a", "b", "c"]
let vals = [1, 2, 3]
let dict = Dictionary(uniqueKeysWithValues: zip(keys, vals))

default: para update sem verificar existência. mapValues transforma valores mantendo chaves. merge combina dicts com closure de conflito. Dictionary(grouping:by:) agrupa por critério. zip + uniqueKeysWithValues cria dict de arrays.

Classes e Structs


9 cards
Structs
struct Ponto {
    var x: Double
    var y: Double

    func distancia(ate outro: Ponto) -> Double {
        let dx = x - outro.x
        let dy = y - outro.y
        return (dx*dx + dy*dy).squareRoot()
    }
}

// Memberwise init automático:
var p = Ponto(x: 3, y: 4)

// Value type (cópia independente):
var q = p
q.x = 10
print(p.x)  // 3 (inalterado!)

// Mutação requer var:
p.x = 5  // OK (var)

struct é value type — cada cópia é independente. Recebe memberwise init gratuitamente. Preferida para modelos de dados. Mutação requer var. Copy-on-write para eficiência. É o tipo mais usado em Swift moderno.

Extensions
extension Int {
    var isPar: Bool { self % 2 == 0 }
    var aoQuadrado: Int { self * self }

    func repetido(_ vezes: Int) {
        for _ in 0..<vezes { print(self) }
    }
}

4.isPar       // true
5.aoQuadrado  // 25
3.repetido(2) // imprime 3 duas vezes

// Extensão de String:
extension String {
    var capitalizado: String {
        prefix(1).uppercased() + dropFirst()
    }
}
"swift".capitalizado  // "Swift"

extension adiciona funcionalidade a tipos existentes (incluindo do sistema). Pode adicionar propriedades computadas, métodos e conformância a protocolos. Não pode adicionar stored properties. Ideal para utilitários e DSLs.

Mutating e static
struct Contador {
    var valor = 0

    // mutating: modifica self em struct
    mutating func incrementar() {
        valor += 1
    }

    mutating func reset() {
        self = Contador()  // substituir self
    }
}

var c = Contador()
c.incrementar()  // OK (var)

// Static (tipo, não instância):
struct Math {
    static let pi = 3.14159
    static func dobro(_ x: Int) -> Int { x * 2 }
}
Math.pi        // 3.14159
Math.dobro(5)  // 10

// Em classes: class func (overridable)

mutating permite que métodos de struct modifiquem propriedades. Sem mutating, struct methods são read-only. static pertence ao tipo (não à instância). class func em classes é overridable. self = ... substitui toda a struct.

Classes e herança
class Animal {
    let nome: String
    init(nome: String) { self.nome = nome }
    func som() -> String { "..." }
}

class Cao: Animal {
    override func som() -> String { "Au!" }
}

let c = Cao(nome: "Rex")
print(c.som())  // "Au!"

// Reference type (partilhado):
let a = c
a.nome  // "Rex" — mesmo objeto!

// Deinitializer:
class Ficheiro {
    deinit { print("Fechado") }
}

// Verificar tipo:
c is Animal      // true
c as? Animal     // Animal?

class é reference type — múltiplas variáveis apontam para o mesmo objeto. Suporta herança (uma base), override e deinit. is verifica tipo, as? faz cast. Usar quando precisa de identidade partilhada ou herança.

Protocol extensions
protocol Numeric {
    var valor: Double { get }
}

// Implementação default:
extension Numeric {
    var aoQuadrado: Double { valor * valor }
    func descricao() -> String {
        "Valor: \(valor), Quadrado: \(aoQuadrado)"
    }
}

struct Moeda: Numeric {
    let valor: Double
}
Moeda(valor: 5).aoQuadrado  // 25.0

// Retroactive conformance:
extension Int: Numeric {
    var valor: Double { Double(self) }
}
42.descricao()  // "Valor: 42.0, Quadrado: 1764.0"

Protocol extensions dão implementação default — qualquer tipo conforme ganha métodos gratuitamente. Permite retroactive conformance (conformar tipos existentes). Base do POP — alternativa à herança clássica. Composição > herança.

Protocolos
protocol Descritivel {
    var descricao: String { get }
    func resumir() -> String
}

struct Livro: Descritivel {
    let titulo: String
    var descricao: String { "Livro: \(titulo)" }
    func resumir() -> String { titulo }
}

// Protocol como tipo:
let itens: [any Descritivel] = [Livro(titulo: "Swift")]

// Múltipla conformância:
struct Produto: Descritivel, Codable, Hashable {
    // implementar todos
}

// Protocol composition:
func mostrar(_ item: Descritivel & Codable) { }

protocol define contratos sem implementação. Qualquer tipo pode conformar. any Protocol (existential) para coleções heterogéneas. Composição com & exige múltiplos protocolos. Base do Protocol-Oriented Programming.

Properties e observers
struct Circulo {
    var raio: Double

    // Computed property:
    var area: Double { .pi * raio * raio }

    // Getter/Setter:
    var diametro: Double {
        get { raio * 2 }
        set { raio = newValue / 2 }
    }
}

// Property observers:
class Config {
    var volume: Int = 50 {
        willSet { print("De \(volume) para \(newValue)") }
        didSet { print("Mudou de \(oldValue)") }
    }
}

// Lazy (calculada no 1º acesso):
struct DB {
    lazy var conexao = Conexao()  // só cria quando usado
}

Computed properties calculam on-demand. willSet/didSet observam mudanças. lazy adia inicialização até 1º acesso. newValue e oldValue são implícitos. Ideal para validação, logging e cache.

Enums com associated values
enum Resultado {
    case sucesso(dados: String)
    case erro(codigo: Int, msg: String)
    case carregando
}

let r = Resultado.sucesso(dados: "JSON")

switch r {
case .sucesso(let dados):
    print("Dados: \(dados)")
case .erro(let cod, let msg):
    print("Erro \(cod): \(msg)")
case .carregando:
    print("Aguarde...")
}

// Raw values:
enum HTTPMethod: String {
    case get = "GET"
    case post = "POST"
}
HTTPMethod.get.rawValue  // "GET"

Enums em Swift são poderosos — cada caso pode ter associated values de tipos diferentes. Substituem hierarquias de classes para estados. Pattern matching no switch extrai valores. Raw values para enums simples (String, Int).

Subscripts
struct Matrix {
    let rows: Int, cols: Int
    var data: [Double]

    subscript(row: Int, col: Int) -> Double {
        get { data[row * cols + col] }
        set { data[row * cols + col] = newValue }
    }
}

var m = Matrix(rows: 2, cols: 2, data: [1,2,3,4])
m[0, 1]      // 2.0
m[1, 0] = 9  // set

// Subscript com tipo diferente:
extension String {
    subscript(i: Int) -> Character {
        self[index(startIndex, offsetBy: i)]
    }
}
"Swift"[0]  // "S"

subscript permite acesso por índice como arrays. Pode ter múltiplos parâmetros e tipos. Getter e setter opcionais. Ideal para matrizes, grids e coleções custom. Sintaxe natural: m[0, 1] em vez de m.get(0, 1).

Avançado


9 cards
Generics avançados
struct Stack<Element> {
    private var items: [Element] = []
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element? {
        items.popLast()
    }
}

var stack = Stack<Int>()
stack.push(1)
stack.push(2)
stack.pop()  // 2

// Associated types:
protocol Container {
    associatedtype Item
    mutating func add(_ item: Item)
    var count: Int { get }
}

// Opaque types (some):
func makeShape() -> some Shape {
    Circle(radius: 5)  // tipo concreto escondido
}

Generics permitem código reutilizável com type safety. associatedtype em protocolos define tipos abstractos. some Protocol (opaque type) esconde o tipo concreto mas mantém performance. any Protocol (existential) permite heterogeneidade.

Property wrappers
@propertyWrapper
struct Clamped {
    private var value: Int
    let range: ClosedRange<Int>

    var wrappedValue: Int {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }

    init(wrappedValue: Int, range: ClosedRange<Int>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }
}

struct Config {
    @Clamped(range: 0...100) var volume = 50
}

var c = Config()
c.volume = 150
print(c.volume)  // 100 (clamped!)

Property wrappers encapsulam lógica de getter/setter reutilizável. @Clamped restringe valores automaticamente. wrappedValue é a propriedade. Usados em SwiftUI: @State, @Binding, @ObservedObject. Eliminam validação repetitiva.

Sendable e data races
// Sendable: tipo seguro para concorrência
struct Ponto: Sendable {
    let x, y: Double
}

// Structs com let são Sendable automaticamente
// Classes precisam de @unchecked ou actor

// @Sendable closure:
func executar(_ work: @Sendable () async -> Void) {
    Task { await work() }
}

// Non-Sendable (cuidado!):
class Cache {  // NÃO é Sendable
    var data: [String: Any] = [:]
}

// Solução: actor
actor SafeCache {
    var data: [String: Any] = [:]
}

// Swift 6: strict concurrency checking
// Data races = erro de compilação!

Sendable marca tipos seguros para passar entre tasks. Structs imutáveis são Sendable automaticamente. Classes mutáveis NÃO são — usar actor. @Sendable em closures. Swift 6 torna data races em erros de compilação (strict concurrency).

Error handling avançado
enum NetworkError: Error, LocalizedError {
    case semConexao
    case timeout(segundos: Int)
    case servidor(code: Int)

    var errorDescription: String? {
        switch self {
        case .semConexao: "Sem conexão"
        case .timeout(let s): "Timeout: \(s)s"
        case .servidor(let c): "Erro \(c)"
        }
    }
}

// try? e try!:
let dados = try? fetch()   // Optional
let dados2 = try! fetch()  // crash se erro!

// rethrows:
func process(_ f: () throws -> Int) rethrows -> Int {
    try f()
}

// Never (não retorna):
func fatalError(_ msg: String) -> Never {
    print(msg)
    exit(1)
}

LocalizedError adiciona mensagens legíveis. try? converte em optional. try! crasha (evitar). rethrows só lança se o argumento lançar. Never indica que não retorna. Enums com associated values para erros ricos.

Result builders
@resultBuilder
struct HTMLBuilder {
    static func buildBlock(_ parts: String...) -> String {
        parts.joined(separator: "\n")
    }

    static func buildOptional(_ part: String?) -> String {
        part ?? ""
    }
}

func html(@HTMLBuilder content: () -> String) -> String {
    "<html>\n\(content())\n</html>"
}

let showFooter = true
let page = html {
    "<h1>Título</h1>"
    "<p>Conteúdo</p>"
    if showFooter {
        "<footer>Fim</footer>"
    }
}

Result builders permitem sintaxe declarativa (DSL) — a base do SwiftUI. buildBlock combina expressões. buildOptional suporta if. O compilador transforma o bloco em chamadas ao builder. Usado em SwiftUI, Vapor (HTML), e DSLs custom.

Async/Await e TaskGroup
// Concorrência estruturada:
func fetchAll(urls: [URL]) async throws -> [Data] {
    try await withThrowingTaskGroup(of: Data.self) { group in
        for url in urls {
            group.addTask {
                try await URLSession.shared.data(from: url).0
            }
        }
        return try await group.reduce(into: []) { $0.append($1) }
    }
}

// Task com prioridade:
Task(priority: .high) {
    let dados = try await fetchDados()
}

// Sleep:
try await Task.sleep(for: .seconds(2))

// Cancelamento:
let task = Task { try await trabalho() }
task.cancel()

TaskGroup executa tarefas em paralelo com tratamento unificado. withThrowingTaskGroup para funções que lançam. Task(priority:) define prioridade. Task.sleep suspende. task.cancel() cancela. Concorrência estruturada = sem callbacks.

Memory management (ARC)
// ARC: Automatic Reference Counting
class Node {
    let value: Int
    var next: Node?       // strong ref
    weak var prev: Node?  // weak ref (não conta!)

    init(_ v: Int) { value = v }
    deinit { print("Node \(value) libertado") }
}

var a: Node? = Node(1)
var b: Node? = Node(2)
a?.next = b       // strong: b retido
b?.prev = a       // weak: não retém a

a = nil           // Node 1 libertado (sem strong refs)
b = nil           // Node 2 libertado

// Closures: capture list
var handler: (() -> Void)?
let obj = Objeto()
handler = { [weak obj] in
    guard let obj else { return }
    obj.fazer()
}

ARC conta referências strong. Quando chega a 0, liberta. weak não conta (evita ciclos). unowned como weak mas crasha se nil. Closures capturam strong por default — usar [weak self] para evitar retain cycles. deinit para cleanup.

Actors
actor BankAccount {
    var balance: Double = 0

    func deposit(_ amount: Double) {
        balance += amount
    }

    func withdraw(_ amount: Double) throws -> Double {
        guard balance >= amount else {
            throw BankError.insuficiente
        }
        balance -= amount
        return balance
    }
}

// Acesso requer await:
let account = BankAccount()
await account.deposit(100)
let novo = try await account.withdraw(50)

// MainActor (UI thread):
@MainActor
class ViewModel: ObservableObject {
    @Published var items: [String] = []
}

actor protege estado mutável de data races automaticamente. Só uma task acede ao estado de cada vez. Acesso externo requer await. @MainActor garante execução na thread principal (UI). Substitui locks manuais com segurança do compilador.

Macros (Swift 5.9+)
// Macros geram código em compilação:

// Attached macro:
@Observable
class ViewModel {
    var items: [String] = []
    var selected: String?
}
// Gera: observation tracking automático

// Freestanding macro:
let url = #URL("https://swift.org")
// Valida URL em compilação!

// #Preview macro (SwiftUI):
#Preview {
    ContentView()
}

// Custom macro (sintaxe):
// @attached(member)
// public macro Model() = #externalMacro(...)

// Macros são expandidos em compilação
// (não em runtime) — zero overhead

Macros (Swift 5.9+) geram código em compilação. @Observable substitui ObservableObject. #URL valida em compilação. #Preview para SwiftUI. Attached (@) vs freestanding (#). Zero overhead — expandidos pelo compilador.

Instalação e Setup


9 cards
Instalar Xcode (macOS)
// 1. Instalar Xcode na App Store (gratuito)
// 2. Instalar Command Line Tools:
xcode-select --install

// 3. Verificar instalação:
swift --version
// Swift version 6.0 (swiftlang-6.0.0)

// 4. Aceitar licença:
sudo xcodebuild -license accept

// Xcode inclui:
// - Compilador Swift (swiftc)
// - REPL (swift)
// - Package Manager (swift package)
// - Simuladores iOS/watchOS/tvOS
// - Interface Builder e SwiftUI Preview

Xcode é o IDE oficial (só macOS). xcode-select --install instala as CLI tools. swift --version confirma a instalação. Inclui compilador, REPL, simuladores e Swift Package Manager. Necessário para desenvolvimento iOS/macOS.

Dependências (SPM)
// Package.swift
// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "MeuApp",
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire.git",
                 from: "5.9.0"),
        .package(url: "https://github.com/apple/swift-log.git",
                 from: "1.5.0"),
    ],
    targets: [
        .executableTarget(
            name: "MeuApp",
            dependencies: ["Alamofire",
                           .product(name: "Logging",
                                    package: "swift-log")]
        )
    ]
)

Package.swift declara dependências com .package(url:from:). from: usa semantic versioning. .product(name:package:) quando o nome do produto difere do pacote. swift package resolve faz download. Dependências ficam em .build/.

Ferramentas essenciais
// sourcekit-lsp: language server (editores)
// Integrado no Xcode, VS Code, Neovim

// swift-format: formatador oficial
swift format --in-place Sources/

// swift-docc: documentação
swift package generate-documentation

// Instruments: profiling (Xcode)
// Product → Profile → Time Profiler

// lldb: debugger
$ lldb ./app
(lldb) breakpoint set --line 10
(lldb) run
(lldb) print variavel
(lldb) po objeto  // object description

// swift-testing (Swift 6):
// import Testing
// @Test func teste() { #expect(2 + 2 == 4) }

sourcekit-lsp dá autocomplete em editores. swift-format formata código. Instruments faz profiling (memória, CPU). lldb é o debugger (po imprime objetos). swift-testing é o framework moderno de testes (substitui XCTest).

Swift em Linux/Windows
// Linux (Ubuntu/Debian):
// 1. Download em swift.org/download
// 2. Extrair e adicionar ao PATH:
export PATH=/usr/local/swift/usr/bin:$PATH

// 3. Verificar:
swift --version

// Windows:
// 1. Instalar Visual Studio 2022 (C++ workload)
// 2. Download installer em swift.org
// 3. Instalar e usar via Developer Command Prompt

// Docker:
docker run --rm swift:6.0 swift --version

// Nota: SwiftUI/UIKit só disponíveis no macOS
// Em Linux: Foundation, networking, server-side

Swift é open-source e funciona em Linux e Windows. Download em swift.org. Docker tem imagens oficiais. Em Linux/Windows: sem SwiftUI/UIKit (só server-side e CLI). Ideal para backend com Vapor ou Hummingbird.

Hello World e estrutura
// main.swift (ou top-level em scripts)
import Foundation

print("Olá, Mundo!")

// Sem main() explícito em scripts
// Em SPM executable: main.swift ou @main

// Com @main (Swift 5.3+):
@main
struct App {
    static func main() {
        print("Olá com @main!")
    }
}

// Comentários:
// Linha única
/* Multi-linha
   /* aninhado! */ */
/// Documentação (aparece no Quick Help)

print() é a função de output. Scripts executam top-level sem main(). @main define ponto de entrada em projetos SPM. import Foundation para tipos básicos avançados. Comentários: //, /* */ (aninháveis), /// para documentação.

Swift REPL e scripts
// REPL interativo:
$ swift
Welcome to Swift!
1> let x = 42
2> print(x * 2)
84
3> :quit

// Script (ficheiro .swift):
// ola.swift
print("Olá, Swift!")
let nums = [1, 2, 3]
print(nums.map { $0 * 2 })

// Executar:
swift ola.swift

// Compilar para binário:
swiftc ola.swift -o ola
./ola

// Modo otimizado:
swiftc -O ola.swift -o ola_release

swift abre o REPL interativo. swift ficheiro.swift executa scripts. swiftc compila para binário nativo. -O ativa otimizações. REPL é ideal para experimentar. Scripts não precisam de main() — código no topo executa diretamente.

Xcode: criar projeto
// 1. Xcode → File → New → Project
// 2. Escolher template:
//    - App (iOS/macOS) → SwiftUI ou UIKit
//    - Command Line Tool → CLI
//    - Framework → biblioteca
//    - Package → SPM

// 3. Configurar:
//    Product Name: MeuApp
//    Organization: com.empresa
//    Interface: SwiftUI
//    Language: Swift

// 4. Estrutura do projeto:
//    MeuApp/
//    ├── MeuApp.swift      → @main entry
//    ├── ContentView.swift → UI principal
//    ├── Assets.xcassets   → imagens/cores
//    └── Info.plist        → configuração

// 5. Run: Cmd+R (simulador)

Xcode cria projetos com templates. SwiftUI é o framework moderno de UI. Cmd+R compila e corre no simulador. ContentView.swift é a view principal. Assets.xcassets gere imagens e cores. Info.plist tem configuração da app.

Swift Package Manager
// Criar pacote executável:
swift package init --type executable
cd MeuApp

// Criar biblioteca:
swift package init --type library

// Estrutura:
// Package.swift       → manifesto
// Sources/            → código fonte
// Tests/              → testes
// .build/             → artefactos (gitignore)

// Comandos:
swift build             // compilar
swift run               // compilar + executar
swift test              // correr testes
swift package resolve   // resolver dependências
swift package update    // atualizar dependências

swift package init cria projeto com estrutura padrão. Package.swift é o manifesto (dependências, targets). swift build compila, swift run executa. swift test corre testes. Substitui Xcode projects para CLI e server-side.

Compilação e otimização
// Compilar ficheiro único:
swiftc main.swift -o app

// Múltiplos ficheiros:
swiftc *.swift -o app

// Otimizações:
swiftc -O main.swift -o app        // otimizar
swiftc -Ounchecked main.swift      // sem checks (risco!)

// Debug info:
swiftc -g main.swift -o app

// Módulos:
swiftc -emit-module -module-name MeuLib *.swift

// Ver tempo de compilação:
swiftc -Xfrontend -debug-time-function-bodies main.swift

// Whole Module Optimization (SPM):
// swift build -c release

swiftc é o compilador. -O ativa otimizações (release). -g adiciona debug info. -Ounchecked remove verificações (perigoso). swift build -c release compila com WMO. Swift compila para código nativo via LLVM.

SwiftUI e Ecossistema


9 cards
SwiftUI: primeira view
import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing: 16) {
            Text("Olá, SwiftUI!")
                .font(.largeTitle)
                .foregroundColor(.blue)

            Button("Tocar") {
                print("Tocado!")
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
    }
}

// App entry:
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

SwiftUI é declarativo — descreve UI, não imperativo. body retorna some View. VStack/HStack/ZStack para layout. Modificadores encadeáveis (.font, .padding). @main + WindowGroup é o entry point.

URLSession (networking)
// GET com async/await:
func fetchUsers() async throws -> [User] {
    let url = URL(string: "https://api.com/users")!
    let (data, response) = try await URLSession.shared
        .data(from: url)

    guard let http = response as? HTTPURLResponse,
          http.statusCode == 200 else {
        throw NetworkError.servidor(code: 500)
    }

    return try JSONDecoder().decode([User].self, from: data)
}

// POST:
func criar(user: User) async throws {
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json",
                     forHTTPHeaderField: "Content-Type")
    request.httpBody = try JSONEncoder().encode(user)
    let _ = try await URLSession.shared.data(for: request)
}

URLSession.shared.data(from:) com async/await. Verificar HTTPURLResponse e status code. JSONDecoder para parse. POST: definir httpMethod, headers e httpBody. Sempre tratar erros com throws. Substitui completion handlers.

Deploy e distribuição
// iOS App Store:
// 1. Xcode → Product → Archive
// 2. Organizer → Distribute App
// 3. App Store Connect (upload)
// 4. TestFlight (beta testing)

// Certificados e Profiles:
// - Apple Developer Account ($99/ano)
// - Signing & Capabilities (Xcode)
// - Provisioning Profiles

// macOS:
// - Mac App Store ou notarização
// - xcrun notarytool submit app.zip

// CI/CD:
// xcodebuild -scheme MyApp -sdk iphonesimulator
// xcodebuild test -scheme MyApp -destination "platform=iOS Simulator,name=iPhone 16"

// fastlane:
// fastlane beta  // upload TestFlight

Distribuição iOS requer Apple Developer Account. Archive + Organizer para upload. TestFlight para beta. Signing com certificados e profiles. macOS: notarização com notarytool. CI/CD com xcodebuild ou fastlane.

@State e @Binding
struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack {
            Text("Contagem: \(count)")
            Button("+1") { count += 1 }
        }
    }
}

// @Binding: passar estado para filho
struct ChildView: View {
    @Binding var value: Int

    var body: some View {
        Button("Reset") { value = 0 }
    }
}

// Uso:
struct ParentView: View {
    @State private var count = 0
    var body: some View {
        ChildView(value: $count)  // $ = binding
    }
}

@State gere estado local da view (private). Mudanças re-renderizam automaticamente. @Binding passa referência mutável para filho. $count cria binding. State é a fonte de verdade. Sempre private em @State.

Combine (reactivo)
import Combine

class ViewModel: ObservableObject {
    @Published var searchText = ""
    @Published var results: [String] = []
    private var cancellables = Set<AnyCancellable>()

    init() {
        $searchText
            .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
            .removeDuplicates()
            .filter { !$0.isEmpty }
            .sink { [weak self] query in
                self?.search(query)
            }
            .store(in: &cancellables)
    }

    func search(_ q: String) {
        results = ["Resultado: \(q)"]
    }
}

Combine é o framework reactivo da Apple. @Published emite mudanças. debounce espera pausa. removeDuplicates evita repetições. sink subscreve. store(in:) mantém a subscrição viva. Alternativa: AsyncSequence (mais moderno).

Navigation
struct ListView: View {
    let items = ["Swift", "SwiftUI", "Combine"]

    var body: some View {
        NavigationStack {
            List(items, id: \.self) { item in
                NavigationLink(value: item) {
                    Text(item)
                }
            }
            .navigationTitle("Tecnologias")
            .navigationDestination(for: String.self) { item in
                DetailView(titulo: item)
            }
        }
    }
}

struct DetailView: View {
    let titulo: String
    var body: some View {
        Text(titulo)
            .navigationTitle(titulo)
    }
}

NavigationStack (iOS 16+) gere navegação. NavigationLink(value:) + navigationDestination(for:) = type-safe navigation. List para listas scrolláveis. navigationTitle define o título. Substitui NavigationView (deprecated).

Testing
import Testing

// swift-testing (Swift 6 / Xcode 16):
@Test func somaCorreta() {
    #expect(soma(2, 3) == 5)
}

@Test("Validação de email")
func emailValido() {
    #expect(validar("ana@mail.com") == true)
    #expect(validar("invalido") == false)
}

// XCTest (clássico):
import XCTest

class MathTests: XCTestCase {
    func testSoma() {
        XCTAssertEqual(soma(2, 3), 5)
    }

    func testErro() throws {
        XCTAssertThrowsError(try parse("")) { error in
            XCTAssertEqual(error as? ParseError, .vazio)
        }
    }
}

swift-testing é o framework moderno (macros, @Test, #expect). XCTest é o clássico (XCTAssertEqual). Ambos rodam com swift test ou Xcode. Testar edge cases, erros e integração. @Test com descrição para clareza.

Codable (JSON)
struct User: Codable {
    let id: Int
    let nome: String
    let email: String
}

// Decode (JSON → Struct):
let json = """
{"id": 1, "nome": "Ana", "email": "ana@mail.com"}
""".data(using: .utf8)!

let user = try JSONDecoder().decode(User.self, from: json)

// Encode (Struct → JSON):
let data = try JSONEncoder().encode(user)
let str = String(data: data, encoding: .utf8)

// CodingKeys (mapear nomes):
struct Post: Codable {
    let titulo: String
    let dataCriacao: Date

    enum CodingKeys: String, CodingKey {
        case titulo = "title"
        case dataCriacao = "created_at"
    }
}

Codable = Encodable + Decodable. JSONDecoder converte JSON → struct. JSONEncoder faz o inverso. CodingKeys mapeia nomes diferentes. Automático para tipos simples. Essencial para APIs REST e persistência.

SwiftData (persistência)
import SwiftData

@Model
class Post {
    var titulo: String
    var corpo: String
    var dataCriacao: Date

    init(titulo: String, corpo: String) {
        self.titulo = titulo
        self.corpo = corpo
        self.dataCriacao = .now
    }
}

// Na App:
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup { ContentView() }
            .modelContainer(for: Post.self)
    }
}

// Na View:
struct ListView: View {
    @Query(sort: \Post.dataCriacao, order: .reverse)
    var posts: [Post]

    @Environment(\.modelContext) var context

    func add() {
        context.insert(Post(titulo: "Novo", corpo: "..."))
    }
}

SwiftData (iOS 17+) é o ORM moderno da Apple. @Model define entidades. @Query busca com sorting/filtering. modelContext para insert/delete. .modelContainer(for:) configura. Substitui Core Data com sintaxe SwiftUI nativa.