Básico
Variáveis & Constantes
Use let por padrão para valores que não mudam; mude para var apenas quando mutação for necessária. Swift infere tipos em tempo de compilação, mas anotações explícitas melhoram a legibilidade para tipos complexos ou ambíguos.
var name = "Alice" // mutable
let age = 30 // immutable constant
let pi: Double = 3.14159 // explicit type
var count: Int = 0
count = 10
print(type(of: age)) // IntOptionals
Optionals representam a ausência de um valor. Use if-let para unwrapping seguro, ?? para valores padrão e ! apenas quando você tem certeza de que o valor existe (arriscado—pode causar crash).
var nickname: String? = nil
nickname = "Al"
if let n = nickname {
print("Hi, \(n)")
}
print(nickname ?? "Anonymous") // nil-coalescing
let force: String = nickname! // force unwrap (crashes if nil)Tuples
Tuples agrupam múltiplos valores em um único valor composto. Úteis para retornar múltiplos valores de funções. Elementos nomeados melhoram a legibilidade.
let person = (name: "Alice", age: 30)
print(person.name) // Alice
print(person.0) // Alice
let (n, a) = person
print("\(n), \(a)") // Alice, 30Conversão de Tipos
Swift não converte implicitamente entre tipos. Sempre use conversão explícita (ex.: Double(intVal)) para evitar ambiguidade e prevenir bugs sutis.
let intVal = 42
let doubleVal = Double(intVal)
let strVal = String(intVal)
let fromStr = Int("100")! // 100
let invalid = Int("abc") // nil
print(doubleVal, strVal)Asserções & Pré-condições
Use assert para verificações de depuração (removidas em builds de release) e precondition para invariantes críticos verificados em todos os builds. Ambos ajudam a capturar erros de lógica cedo.
let age = -5
assert(age >= 0, "Age cannot be negative")
precondition(age >= 0, "Age must be non-negative")
// In debug builds, assert crashes if false
// precondition checks in release builds tooStrings & Caracteres
Interpolação de Strings
Interpolação de strings com \(expr) incorpora qualquer expressão em uma string. É type-safe e avaliada em tempo de compilação, tornando-a mais segura que format strings.
let name = "Alice"
let age = 30
let msg = "Name: \(name), Age: \(age)"
let calc = "5 + 3 = \(5 + 3)"
print(msg) // Name: Alice, Age: 30
print(calc) // 5 + 3 = 8Métodos Comuns de String
Strings em Swift são Unicode-correct, o que significa que count reflete caracteres reais (grapheme clusters), não bytes. Use esses métodos em vez de indexação manual para segurança.
let s = "Hello, World"
print(s.count) // 12
print(s.uppercased()) // HELLO, WORLD
print(s.lowercased()) // hello, world
print(s.hasPrefix("Hello")) // true
print(s.contains("World")) // true
print(s.reversed()) // "dlroW ,olleH"Substring & Indexação
Índices de string não são inteiros porque caracteres podem ter tamanhos de bytes diferentes. Use index(_:offsetBy:) para navegação. Substrings compartilham memória com a original—converta para String para armazenamento de longo prazo.
let s = "Hello, World"
let idx = s.index(s.startIndex, offsetBy: 7)
print(s[idx]) // W
let sub = s[idx...] // "World"
let newStr = String(sub) // convert to String
print(s.prefix(5)) // "Hello"Strings Multiline
Strings com aspas triplas preservam quebras de linha e indentação. As """ de fechamento determinam a indentação base. Ideais para HTML, JSON ou blocos de texto longos.
let poem = """
Roses are red,
Violets are blue,
Swift is great,
And so are you.
"""
print(poem)
// Use \\(expr) for interpolation in multilineConstrução & Divisão de Strings
Use joined(separator:) para concatenar com um delimitador e split(separator:) para tokenizar. Esses são mais eficientes que loops manuais com concatenação +.
let parts = ["apple", "banana", "cherry"]
let joined = parts.joined(separator: ", ")
print(joined) // apple, banana, cherry
let csv = "a,b,c"
let fields = csv.split(separator: ",")
print(fields) // ["a", "b", "c"]
let reversed = String(s.reversed())Estruturas de Dados
Array
Arrays são coleções ordenadas, indexadas a partir de zero. Use append/insert para adições, filter/map/reduce para transformações. Prefira tipos de valor (Array é uma struct) para thread safety.
var nums = [1, 2, 3]
nums.append(4)
nums.insert(0, at: 0)
nums[1] = 20
print(nums.count) // 5
print(nums.contains(20)) // true
let evens = nums.filter { $0 % 2 == 0 }
print(nums.first, nums.last)Dictionary
Dictionaries armazenam pares chave-valor com lookup O(1) médio. Use default: para evitar nil ao acessar chaves ausentes. As chaves devem ser Hashable.
var ages: [String: Int] = ["Alice": 30]
ages["Bob"] = 25
ages["Alice"] = 31
print(ages["Alice", default: 0]) // 31
for (name, age) in ages {
print("\(name): \(age)")
}
print(ages.keys.sorted())Set
Sets armazenam valores únicos com teste de pertencimento O(1). Ideais para deduplicação e operações de conjunto (union, intersection, difference). Os elementos devem ser Hashable.
var a: Set<Int> = [1, 2, 3]
var b: Set<Int> = [3, 4, 5]
print(a.union(b)) // {1,2,3,4,5}
print(a.intersection(b)) // {3}
print(a.subtracting(b)) // {1,2}
print(a.isSubset(of: b)) // false
a.insert(6)Ranges
..< é um intervalo semi-aberto (exclui o limite superior), ... é um intervalo fechado (inclui ambos). Ranges são úteis em loops, slicing e pattern matching com o operador ~=.
for i in 0..<5 { print(i) } // 0,1,2,3,4
for i in 0...5 { print(i) } // 0,1,2,3,4,5
let nums = Array(1...5) // [1,2,3,4,5]
let prefix = nums.prefix(3) // [1,2,3]
if 5...10 ~= 7 { print("in range") }Funções de Ordem Superior
map transforma cada elemento, filter seleciona elementos correspondentes, reduce combina todos em um. Esses são a base da programação funcional em Swift e permitem pipelines de dados concisos e legíveis.
let nums = [1, 2, 3, 4, 5]
let doubled = nums.map { $0 * 2 } // [2,4,6,8,10]
let evens = nums.filter { $0 % 2 == 0 } // [2,4]
let sum = nums.reduce(0, +) // 15
let strs = nums.map { String($0) } // ["1","2",...]
print(doubled, evens, sum)Fluxo de Controle
If / Else If / Else
Branching condicional padrão. As condições devem ser Boolean (tipo Bool). Swift exige chaves mesmo para instruções únicas para evitar bugs como a vulnerabilidade goto fail da Apple.
let score = 85
if score >= 90 {
print("A")
} else if score >= 80 {
print("B")
} else if score >= 70 {
print("C")
} else {
print("F")
}Switch (Pattern Matching)
Switch do Swift é poderoso: suporta value binding, tuples, where guards e ranges. Deve ser exaustivo (default cobre casos restantes) e não faz fall-through por padrão.
let point = (2, 0)
switch point {
case (0, 0):
print("origin")
case (_, 0):
print("on x-axis")
case (0, _):
print("on y-axis")
case let (x, y) where x == y:
print("on diagonal")
default:
print("elsewhere: \(point.0),\(point.1)")
}Loops For-In
For-in itera sobre ranges, arrays, dictionaries e qualquer Sequence. Use enumerated() quando precisar do índice. Use _ para ignorar variáveis de loop que você não usa.
for i in 0..<5 { print(i) }
let fruits = ["apple", "banana"]
for (i, fruit) in fruits.enumerated() {
print("\(i): \(fruit)")
}
let dict = ["a": 1, "b": 2]
for (k, v) in dict { print("\(k)=\(v)") }
for _ in 0..<3 { print("tick") }While & Repeat-While
while verifica a condição antes de cada iteração; repeat-while verifica depois (como do-while em C). Use repeat-while quando o corpo deve executar pelo menos uma vez.
var n = 5
while n > 0 {
print(n)
n -= 1
}
var x = 0
repeat {
x += 1
} while x < 3
print(x) // 3Guard (Saída Antecipada)
guard fornece saída antecipada quando pré-requisitos não são atendidos. Optionals unwrapped permanecem disponíveis no resto do escopo, reduzindo aninhamento. Prefira guard a aninhamento profundo de if-let.
func greet(_ name: String?) {
guard let n = name, !n.isEmpty else {
print("No name provided")
return
}
print("Hello, \(n)")
// n is unwrapped and available here
}
greet("Alice")
greet(nil)Funções & Closures
Definição de Função
Use _ para omitir argument labels para legibilidade. Valores padrão de parâmetros tornam parâmetros opcionais. Funções com uma única expressão têm retorno implícito (Swift 5.9+).
func greet(_ name: String, greeting: String = "Hello") -> String {
return "\(greeting), \(name)!"
}
print(greet("Alice")) // Hello, Alice!
print(greet("Bob", greeting: "Hi")) // Hi, Bob!
func add(_ a: Int, _ b: Int) -> Int { a + b }
print(add(3, 4)) // 7Múltiplos Valores de Retorno
Tuples permitem retornar múltiplos valores nomeados. Retornar uma tuple optional sinaliza possível falha. Acesse via r.min ou r.0—elementos nomeados são mais claros.
func minMax(_ nums: [Int]) -> (min: Int, max: Int)? {
guard let first = nums.first else { return nil }
var mn = first, mx = first
for n in nums { mn = min(mn, n); mx = max(mx, n) }
return (mn, mx)
}
if let r = minMax([3, 1, 4, 1, 5]) {
print("min=\(r.min), max=\(r.max)")
}Closures
Closures são blocos autônomos de funcionalidade. Use $0, $1 para shorthand argument names. Trailing closure syntax (omitindo o último argument label) é idiomática para map/filter/reduce.
let square: (Int) -> Int = { x in x * x }
print(square(5)) // 25
let add: (Int, Int) -> Int = { $0 + $1 }
print(add(3, 4)) // 7
let nums = [1, 2, 3]
let doubled = nums.map { $0 * 2 }
print(doubled) // [2, 4, 6]Escaping & Auto-Closures
@escaping marca closures armazenadas ou chamadas após a função retornar (necessário para callbacks async). @autoclosure envolve uma expressão em uma closure, atrasando sua avaliação (usado em assert).
var handlers: [() -> Void] = []
func saveHandler(_ fn: @escaping () -> Void) {
handlers.append(fn)
}
saveHandler { print("done") }
handlers.first?()
// @autoclosure delays evaluation
func logIfTrue(_ cond: @autoclosure () -> Bool) {
if cond() { print("true") }
}
logIfTrue(2 > 1)Parâmetros Inout
Parâmetros inout permitem que funções modifiquem a variável do chamador (passagem por referência). Use com moderação—prefira retornar novos valores para clareza. O prefixo & marca o local de mutação.
func swap(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
var x = 10, y = 20
swap(&x, &y)
print(x, y) // 20 10Classes & Structs
Struct (Tipo de Valor)
Structs são tipos de valor—copiadas na atribuição. Use para contêineres de dados simples. Propriedades computadas (distance) calculam no acesso. Métodos mutating modificam self.
struct Point {
var x: Double
var y: Double
var distance: Double {
(x * x + y * y).squareRoot()
}
mutating func moveBy(dx: Double, dy: Double) {
x += dx; y += dy
}
}
var p = Point(x: 3, y: 4)
print(p.distance) // 5.0Class (Tipo de Referência)
Classes são tipos de referência—compartilhadas via referências, suportam herança e deinit. Use quando precisar de identidade, estado mutável compartilhado ou interoperabilidade com Objective-C. Caso contrário, prefira structs.
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
deinit { print("\(name) deallocated") }
}
let p = Person(name: "Alice", age: 30)
print(p.name) // AliceHerança & Override
Use override para redefinir um método de superclasse. Swift usa dynamic dispatch para que a versão da subclasse seja chamada. Marque métodos como final para evitar overriding adicional por desempenho.
class Animal {
func speak() { print("...") }
}
class Dog: Animal {
override func speak() { print("Woof!") }
}
class Cat: Animal {
override func speak() { print("Meow!") }
}
let pets: [Animal] = [Dog(), Cat()]
for pet in pets { pet.speak() }Propriedades (Computadas & Lazy)
Propriedades computadas têm blocos get/set; set usa newValue por padrão. lazy adia a inicialização até o primeiro acesso—útil para propriedades caras ou raramente necessárias. Deve ser var.
class Circle {
var radius: Double
var area: Double {
get { .pi * radius * radius }
set { radius = (newValue / .pi).squareRoot() }
}
lazy var heavy = loadExpensiveData()
init(radius: Double) { self.radius = radius }
}
func loadExpensiveData() -> String { "data" }Property Observers
willSet/didSet observam mudanças de propriedade. Use didSet para validar ou reagir a mudanças (ex.: clamp valores, disparar atualizações de UI). Observers não disparam durante init.
class Counter {
var count: Int = 0 {
willSet { print("about to set \(newValue)") }
didSet {
print("was \(oldValue), now \(count)")
if count > 10 { count = 10 }
}
}
}
let c = Counter()
c.count = 5
c.count = 20 // clamped to 10Protocols & Extensions
Definição de Protocol
Protocols definem um blueprint de métodos e propriedades. Tipos conformam implementando-os. Use para abstração, polimorfismo e desacoplamento—semelhante a interfaces em Java/C#.
protocol Greetable {
var name: String { get }
func greet() -> String
}
struct User: Greetable {
let name: String
func greet() -> String { "Hi, \(name)" }
}
let u = User(name: "Alice")
print(u.greet()) // Hi, AliceProtocol Extensions (Impl Padrão)
Protocol extensions fornecem implementações padrão. Isso permite modelagem retroativa e reuso de código sem uma classe base. Um recurso poderoso para compartilhar comportamento entre tipos não relacionados.
protocol Describable {
func describe() -> String
}
extension Describable {
func describe() -> String { "A \(type(of: self))" }
}
struct Box: Describable { }
let b = Box()
print(b.describe()) // A BoxExtensions
Extensions adicionam funcionalidade a tipos existentes (mesmo os que você não possui, como Int). Use para organizar código, adicionar propriedades computadas ou conformar a protocols. Não pode adicionar stored properties.
extension Int {
var squared: Int { self * self }
func times(_ block: () -> Void) {
for _ in 0..<self { block() }
}
}
print(5.squared) // 25
3.times { print("hi") } // prints hi 3 timesGenerics
Generics escrevem código flexível e reutilizável que funciona com qualquer tipo, preservando a segurança de tipo. Use <T> para parâmetros de tipo. Constraints (where T: Equatable) restringem tipos permitidos.
func stackOf<T>(_ items: T...) -> [T] {
var arr: [T] = []
for item in items { arr.append(item) }
return arr
}
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ e: Element) { items.append(e) }
}
let s = stackOf(1, 2, 3) // [Int]Protocol com Associated Type
Associated types permitem que protocols usem tipos placeholder, como generics para protocols. O tipo conforming especifica o tipo real. Use typealias para torná-lo explícito, ou deixe Swift inferir.
protocol Container {
associatedtype Item
var count: Int { get }
mutating func append(_ item: Item)
subscript(i: Int) -> Item { get }
}
struct IntStack: Container {
typealias Item = Int
private var items: [Int] = []
var count: Int { items.count }
mutating func append(_ item: Int) { items.append(item) }
subscript(i: Int) -> Int { items[i] }
}Tratamento de Erros
Definindo & Lançando Erros
Erros conformam ao protocol Error (geralmente enums). throw sinaliza um erro. Associated values (coinsNeeded) carregam contexto. Use throws para marcar funções que podem falhar.
enum VendingError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
func vend(item: String, coins: Int) throws -> String {
guard item == "Candy" else { throw VendingError.invalidSelection }
guard coins >= 2 else { throw VendingError.insufficientFunds(coinsNeeded: 2) }
return "Dispensing \(item)"
}Do-Catch & Try
do-catch trata erros lançados. try marca chamadas que lançam. Pattern-match em cases específicos para tratamento direcionado. Um catch-all trata erros inesperados. Erros se propagam pela call stack.
do {
let result = try vend(item: "Candy", coins: 1)
print(result)
} catch VendingError.insufficientFunds(let needed) {
print("Need \(needed) more coins")
} catch VendingError.outOfStock {
print("Sold out")
} catch {
print("Other error: \(error)")
}Try? / Try!
try? converte erros em nil (retorna Optional). try! assume sucesso e causa crash em erro—use apenas quando a falha é verdadeiramente impossível. Prefira try? com optional binding para degradação graciosa.
let result1 = try? vend(item: "Candy", coins: 1)
print(result1) // nil (error converted to optional)
let result2 = try! vend(item: "Candy", coins: 5)
print(result2) // Dispensing Candy (crashes if error)
if let r = try? vend(item: "Candy", coins: 5) {
print(r)
}Tipo Result
Result codifica sucesso ou falha como um valor, permitindo tratamento de erro async sem throw. Use para callbacks, APIs async ou quando quiser armazenar/encadear resultados. .get() lança em caso de falha.
enum FetchError: Error { case network, parse }
func fetch(_ url: String) -> Result<String, FetchError> {
url.isEmpty ? .failure(.network) : .success("data from \(url)")
}
switch fetch("https://api.com") {
case .success(let data): print(data)
case .failure(let err): print("Error: \(err)")
}
let value = try? fetch("").get()Defer (Limpeza)
defer agenda código de limpeza para executar quando o escopo sair, independentemente de como (retorno normal, throw, erro). Use para liberação de recursos (arquivos, locks). Múltiplos defers executam em ordem LIFO.