Especificidades do Swift no desenvolvimento móvel: o que é, principais recursos e como funciona

Autor: IT Sectr Publicado: 2026-06-25 Tempo de leitura: 17 min

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

  • 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 e deinit.
  • Actor — tipo de referência thread-safe com isolamento em nível de compilador. O compilador impede o acesso concorrente ao estado mutável de diferentes tarefas.
  • Property Wrappers — @Published, @State, @Binding encapsulam a lógica de propriedades no SwiftUI e Combine, eliminando código boilerplate.
  • Generics permitem escrever funções e estruturas type-safe sem vincular a um tipo específico. A Programação Orientada a Protocolos usa extension + protocol em vez de hierarquia de classes.
  • Enum com valores associados — uma ferramenta poderosa para modelar estados. Cada caso pode armazenar um conjunto arbitrário de dados de diferentes tipos.

Struct vs Class no Swift: tipos de valor e tipos de referência

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.

Semântica de Valor da Struct

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.

swift
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

Semântica de Referência da Class

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.

swift
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 — segurança de threads no Swift

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.

Isolamento de Actor

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.

swift
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 Wrappers: @Published, @State, @Binding

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 e @State

@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.

swift
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.

swift
@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 e Programação Orientada a Protocolos

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.

Restrições Genéricas e Tipos Associados

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.

swift
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 e Implementações Padrão

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.

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 com valores associados e Extension

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 e Pattern Matching

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.

swift
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")
}

Extension para Enum

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.

swift
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

Como struct difere de class no Swift?

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.

O que é Actor no Swift e quando usar?

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.

Quais Property Wrappers são usados no SwiftUI?

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.

O que é Programação Orientada a Protocolos?

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.

Como enum no Swift difere de enum em outras linguagens?

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

  • Struct vs Class — diferença fundamental: tipos de valor são copiados, tipos de referência são passados por referência. Struct não suporta herança, class sim. Apple recomenda struct por padrão.
  • Actor — segurança de threads em nível de compilador. Estado mutável isolado, acesso via await, proteção contra condições de corrida sem bloqueios manuais.
  • Property Wrappers — @Published, @State, @Binding e personalizados via @propertyWrapper. Reduzem código boilerplate e tornam a intenção explícita através de anotação.
  • Generics — funções e tipos type-safe com restrições where. Protocol + tipo associado para abstrações. A biblioteca padrão do Swift é inteiramente construída sobre generics.
  • Programação Orientada a Protocolos — composição de comportamento via extension de protocolo. Implementações padrão permitem reutilização de código sem herança de classes.
  • Enum com valores associados — um modelo poderoso para estados. Cada caso armazena dados únicos. Pattern matching garante que todos os estados são tratados.
  • Extension — adiciona métodos, propriedades computadas e conformidade a protocolos para qualquer tipo, incluindo enum. O código permanece modular e limpo sem alterar a declaração original.

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.

Discutir o projeto