Swift é a linguagem de programação da Apple que combina o poder do Objective-C com a segurança das linguagens modernas. Ao contrário de muitas outras linguagens, o Swift oferece construções únicas: tipos de valor com cópia automática, Actores com isolamento em nível de compilador, Property Wrappers para reutilizar lógica de propriedades e Programação Orientada a Protocolos como alternativa à herança clássica. De acordo com a Documentação Apple Swift, esses recursos tornam o código mais seguro e eficiente.
Principais Pontos
No Swift, struct é um tipo de valor, enquanto class é um tipo de referência. Essa diferença fundamental determina o comportamento durante a atribuição, passagem para funções e gerenciamento de memória. Struct é copiada inteiramente em cada atribuição, class é passada por referência — apenas o ponteiro para o objeto no heap é copiado.
Struct implementa automaticamente a semântica de valor — cada instância é independente. A mutação de uma propriedade de struct só é possível através de var, e métodos que modificam propriedades devem ser marcados como mutating. Struct não suporta herança mas pode estar em conformidade com protocolos. A biblioteca padrão do Swift usa struct para String, Array, Dictionary, Int, Bool — todos os tipos fundamentais são tipos de valor.
struct User {
let id: Int
var name: String
mutating func updateName(_ newName: String) {
name = newName
}
}
var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// Cópia completa — instâncias independentes
Class é armazenada no heap e suporta igualdade por referência. Múltiplas variáveis podem referenciar o mesmo objeto, alterações através de uma referência são visíveis através de todas as outras. Class suporta herança, permite sobrescrita de métodos (override), tem deinit para limpeza de recursos. No Swift, class é usada com menos frequência que struct — a Apple recomenda começar com struct e mudar para class apenas quando herança ou semântica de referência forem necessárias.
class Car {
let model: String
var speed: Int
init(model: String, speed: Int) {
self.model = model
self.speed = speed
}
func accelerate(_ amount: Int) {
speed += amount
}
}
let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// Mesma referência — alterações visíveis em todos os lugares
A escolha entre struct e class depende da semântica dos dados. Para configurações, modelos de dados e estados use struct — a cópia é mais segura e previsível. Para objetos com identidade (sessão de usuário, conexão de banco de dados) e quando herança é necessária, use class.
Actor é um tipo de referência que protege seu estado mutável do acesso concorrente. O compilador Swift garante que variáveis de actor só podem ser acessadas do mesmo contexto do actor — qualquer acesso externo causa um erro de compilação. Isso previne condições de corrida em nível de linguagem, sem bloqueios manuais.
Todas as propriedades e métodos de actor são isolados por padrão. Chamar um método de actor de fora requer await — o Swift alterna a execução para o actor e vice-versa. O actor sincroniza o acesso sequencialmente: requisições de diferentes tarefas são colocadas em fila. Isso garante estado consistente dentro do actor. Métodos não isolados (nonisolated) podem ser chamados sem await mas não podem acessar propriedades mutáveis.
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func withdraw(amount: Double) throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
}
nonisolated func accountInfo() -> String {
return "Conta Bancária"
}
}
let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — atômico, sem condição de corrida
Actor é útil para gerenciadores de estado, caches e serviços de rede onde múltiplas tarefas leem e escrevem dados concorrentemente. Ao contrário da sincronização manual via NSLock ou DispatchQueue, actor é verificado em tempo de compilação — é impossível cometer um erro. Erros de isolamento aparecem em tempo de compilação, não em tempo de execução.
Property Wrapper é uma anotação que adiciona comportamento a uma propriedade sem alterar sua declaração. SwiftUI e Combine fornecem wrappers integrados para gerenciar estado de visualizações. O compilador gera código que envolve o acesso à propriedade, adicionando lógica de observação, sincronização ou transformação.
@Published é usado em classes em conformidade com ObservableObject para publicar alterações automaticamente. Assinantes do Combine recebem notificações a cada atualização. @State é o estado local de uma visualização SwiftUI. O SwiftUI redesenha automaticamente a visualização quando @State muda. @Binding cria uma conexão bidirecional entre uma visualização pai e filha sem possuir os dados.
import SwiftUI
import Combine
@main
struct CounterApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class CounterViewModel: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
struct ContentView: View {
@State private var greeting: String = "Olá"
@StateObject private var viewModel = CounterViewModel()
var body: some View {
VStack {
CounterDisplay(count: $viewModel.count)
Text(greeting)
Button("Incrementar", action: viewModel.increment)
}
}
}
struct CounterDisplay: View {
@Binding var count: Int
var body: some View {
Text("Contagem: \(count)")
}
}
Property Wrappers personalizados são criados usando @propertyWrapper e uma struct com um campo wrappedValue. Isso permite reutilizar lógica: validação, normalização, armazenamento UserDefaults. Property Wrappers reduzem significativamente o código boilerplate e tornam a intenção do desenvolvedor explícita através de anotação.
@propertyWrapper
struct Clamped<T: Comparable> {
private var value: T
let range: ClosedRange<T>
init(wrappedValue: T, range: ClosedRange<T>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: T {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
}
struct Settings {
@Clamped(range: 0...100) var volume: Int = 50
}
var s = Settings()
s.volume = 150
// s.volume === 100 — valor automaticamente limitado
Generics é um mecanismo poderoso do Swift para escrever código flexível sem perder segurança de tipos. Funções e tipos genéricos funcionam com qualquer tipo que atenda às restrições. Programação Orientada a Protocolos (POP) é um paradigma do Swift onde protocol + extension substituem a herança clássica de classes, fornecendo composição de comportamento.
Swift permite restringir parâmetros genéricos com protocolos (cláusulas where). Protocolo com tipo associado é o equivalente genérico para protocolos: o tipo concreto é determinado no momento da conformidade. Esta é a base para coleções e algoritmos type-safe. A biblioteca padrão do Swift usa generics ativamente: Array, Dictionary, Optional — todos são tipos genéricos.
protocol Cacheable {
associatedtype Key: Hashable
associatedtype Value
func get(_ key: Key) -> Value?
mutating func set(_ value: Value, for key: Key)
}
struct MemoryCache<K: Hashable, V>: Cacheable {
private var storage: [K: V] = [:]
typealias Key = K
typealias Value = V
func get(_ key: K) -> V? {
return storage[key]
}
mutating func set(_ value: V, for key: K) {
storage[key] = value
}
}
func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
for (index, item) in array.enumerated() {
if item == target {
return index
}
}
return nil
}
let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — um método genérico para todos os tipos Equatable
Extension de protocolo é um mecanismo chave do POP. Em uma extension pode-se fornecer uma implementação padrão dos métodos do protocolo. Tipos em conformidade com o protocolo obtêm esta implementação gratuitamente e podem sobrescrevê-la se necessário. Isso permite construir hierarquias de comportamento sem herança de classes — uma alternativa à herança múltipla, que não existe no Swift.
protocol Drivable {
var speed: Double { get set }
func drive()
}
extension Drivable {
func drive() {
if speed > 0 {
print("Dirigindo a \(speed) km/h")
}
}
}
struct Bicycle: Drivable {
var speed: Double = 15
}
struct Plane: Drivable {
var speed: Double = 900
func drive() {
print("Voando a \(speed) km/h")
}
}
let bike = Bicycle()
bike.drive() // "Dirigindo a 15.0 km/h" — padrão
let plane = Plane()
plane.drive() // "Voando a 900.0 km/h" — sobrescrito
POP com extension permite composição através de múltiplos protocolos — um tipo pode estar em conformidade com vários protocolos, obtendo suas implementações. Isso é mais flexível que a herança única de classes, onde toda a hierarquia é fixa. A biblioteca padrão do Swift é construída sobre POP: CustomStringConvertible, Equatable, Hashable, Codable — todos são protocolos com extensions.
Enum no Swift é uma construção significativamente mais poderosa do que em outras linguagens. Cada caso pode ter valores associados — um conjunto arbitrário de dados de qualquer tipo. Combinado com extension e pattern matching, enum se torna a base para modelar estados, resultados de operações e máquinas de estado finito.
Valores associados permitem que cada caso armazene dados únicos. Por exemplo, o caso loading não contém dados, success contém o resultado, failure contém um erro. Pattern matching via switch extrai os valores associados e trata cada caso. O compilador verifica a exaustividade (exhaustive switch) — isso elimina estados esquecidos.
enum NetworkResult<T> {
case loading
case success(T)
case failure(Error)
}
enum MediaFile {
case image(url: URL, width: Int, height: Int)
case video(url: URL, duration: Double)
case audio(url: URL, bitrate: Int)
}
func handle(_ result: NetworkResult<String>) {
switch result {
case .loading:
print("Carregando...")
case .success(let data):
print("Recebido: \(data)")
case .failure(let error):
print("Erro: \(error.localizedDescription)")
}
}
let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)
if case .video(let url, let duration) = file {
print("Vídeo \(url) duração \(duration)s")
}
Swift permite adicionar métodos, propriedades computadas e conformidade a protocolos via extension para enum. Isso não requer alterar a declaração original — o código permanece limpo e modular. Métodos em extension podem trabalhar com valores associados, calcular dados derivados e implementar lógica de negócio.
extension MediaFile {
var fileName: String {
switch self {
case .image(let url, _, _),
.video(let url, _),
.audio(let url, _):
return url.lastPathComponent
}
}
var description: String {
switch self {
case .image(_, let w, let h):
return "Imagem \(w)x\(h)"
case .video(_, let d):
return "Vídeo \(d)s"
case .audio(_, let b):
return "Áudio \(b) kbps"
}
}
}
extension MediaFile: Equatable {
static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
return lhs.fileName == rhs.fileName
}
}
let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "Vídeo 120.0s"
Enum com valores associados + extension é a substituição para classes seladas do Kotlin ou uniões discriminadas de outras linguagens. Swift usa esta combinação para Result (um tipo padrão na biblioteca padrão), Optional (Optional é um enum com .none e .some) e manipulação de estado no SwiftUI. Pattern matching garante que todos os estados são tratados — erros em tempo de execução são impossíveis.
Perguntas Frequentes
Struct — tipo de valor, copiado na atribuição, não suporta herança. Class — tipo de referência, passado por referência, suporta herança, deinit e verificação de identidade (===). A Apple recomenda começar com struct e mudar para class apenas quando a semântica de referência for necessária.
Actor — tipo de referência thread-safe. O compilador impede o acesso concorrente ao estado mutável de diferentes contextos. Use Actor para gerenciadores de estado, caches e serviços acessados por múltiplas tarefas. Chamar métodos de actor requer await.
Principais: @State — estado local da view; @Binding — conexão com dados sem posse; @Published — publicação de alterações no ObservableObject; @StateObject — posse do ObservableObject; @EnvironmentObject — dependência do ambiente. Wrappers personalizados são criados via @propertyWrapper.
POP — um paradigma onde protocol + extension definem comportamento em vez de herança de classe. Um tipo está em conformidade com vários protocolos, obtendo suas implementações gratuitamente. É composição sobre herança — mais flexível e seguro. A biblioteca padrão do Swift (Equatable, Codable) é construída sobre POP.
Enum no Swift suporta valores associados — cada caso pode armazenar dados arbitrários de diferentes tipos. Combinado com pattern matching (switch), isso fornece um modelo poderoso para estados, erros e Domain-Driven Design. Optional e Result são enums padrão do Swift com valores associados.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.