Pular para conteúdo

Aula 18 - Arquitetura MVVM/VIPER com Concorrência Swift ⚡

Objetivo Pedagógico

Objetivo: Conceber a camada de apresentação arquitetada com MVVM usando a nova macro @Observable (Observation Framework), aliada à Concorrência Estruturada do Swift moderna (async/await, Tasks e Actors isolados).


📑 1. Fundamentos Teóricos & Análise Técnica

A linguagem Swift revolucionou o paradigma de concorrência com o fim dos callbacks de encadeamento (Pyramid of Doom) e a introdução da Concorrência Estruturada Nativa.

A evolução arquitetural moderna articula dois grandes pilares: 1. O Framework de Observação (@Observable): - Substitui o protocolo verboso ObservableObject e a propriedade @Published do Combine. - Utiliza macros em tempo de compilação para rastrear o acesso específico a cada propriedade da classe. As Views do SwiftUI agora só recalculam se a propriedade específica que elas leem for modificada, evitando recomposições abrangentes. 2. Atores e Segurança de Dados em Concorrência (Data-Race Safety): - actor: Tipo de referência que protege o acesso ao seu estado interno com sincronização automática. Apenas uma thread pode executar código dentro do ator por vez. - @MainActor: Atributo que garante que mutações de estado e chamadas de UI sejam executadas obrigatoriamente na thread principal (Main Thread), eliminando avisos de concorrência estrita do Swift 6. 3. Tasks e Cancelamento Cooperativo: - As tarefas herdam prioridades do chamador. O cancelamento é cooperativo: rotinas longas devem verificar periodicamente Task.isCancelled para suspender trabalho desnecessário.

📐 Arquitetura Conceitual & Diagrama de Fluxo

graph TD
    View["SwiftUI View"] -->|Invoca Método Assíncrono| VM["ViewModel (@Observable @MainActor)"]
    VM -->|Task Assíncrona| Service["NetworkService (Actor Isolado)"]
    Service -->|Chamada URLSession async| Endpoint["API Remota"]
    Endpoint -->|Payload JSON| Service
    Service -->|Retorna Modelo Decodificado| VM
    VM -->|Atualiza Propriedade do ViewModel| VM
    VM -.->|Notifica Apenas Propriedade Modificada| View
    style View fill:#e1f5fe,stroke:#01579b
    style VM fill:#fff3e0,stroke:#e65100
    style Service fill:#f3e5f5,stroke:#7b1fa2
    style Endpoint fill:#e8f5e9,stroke:#2e7d32

🔍 Pilares e Diretrizes Técnicas

Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Macro @Observable do Swift 5.9+: Rastreamento granular de dependências que elimina a sobrecarga de memória do Combine na camada de apresentação. - Isolamento com @MainActor: Garante formalmente que nenhuma mutação de estado de View ocorra fora da thread principal. - Actors para Proteção de Estado Concorrente: Estrutura nativa do compilador que erradica condições de corrida em memória (Data Races). - Cancelamento Cooperativo de Tasks: Propagação automática de sinais de cancelamento quando a View é desmontada do container de navegação.


🛠️ 2. Implementação Prática em Swift Moderno, Concorrência Estruturada e Macro @Observable

Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:

// TransactionsViewModel.swift (Arquitetura MVVM com @Observable e @MainActor)
import SwiftUI
import Observation

@Observable
@MainActor
final class TransactionsViewModel {
    var transactions: [TransactionItem] = []
    var isLoading: Bool = false
    var errorMessage: String?

    private let service: TransactionActorService

    init(service: TransactionActorService = TransactionActorService()) {
        self.service = service
    }

    func loadTransactions() async {
        isLoading = true
        errorMessage = nil

        do {
            transactions = try await service.fetchRemoteTransactions()
        } catch {
            errorMessage = "Falha ao carregar dados: \(error.localizedDescription)"
        }

        isLoading = false
    }
}

// Actor garantindo isolamento total de threads para o serviço
actor TransactionActorService {
    func fetchRemoteTransactions() async throws -> [TransactionItem] {
        // Simulação de chamada de rede sem bloquear threads de renderização
        try await Task.sleep(nanoseconds: 600_000_000)
        return [
            TransactionItem(merchant: "Apple Store", amount: 1299.00, category: "Hardware"),
            TransactionItem(merchant: "Coffee Shop", amount: 4.50, category: "Alimentação")
        ]
    }
}

💡 Análise Passo a Passo do Código

  1. Macro @Observable: Elimina a necessidade de publicar objetos com Combine e permite rastreamento atômico de propriedades.
  2. Anotação @MainActor na Classe: Confina todas as alterações de variáveis de estado à thread de interface gráfica.
  3. Isolamento com actor TransactionActorService: Garante segurança de acesso concorrente sem a complexidade de semáforos ou NSLock.

🎯 3. Próximos Passos & Sequência Didática