Swift Academy · Aula

Efeitos e Dependências

Lide com efeitos colaterais e injete dependências.

Aula 3 de 413 etapas

Efeitos e Dependências é uma aula grátis de Swift Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Swift Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Swift Academy inclui 4 aulas no total.

Por que existem efeitos

Os redutores devem permanecer puros: dado um estado e uma ação, eles apenas alteram o estado. Mas aplicativos reais precisam de efeitos colaterais, como solicitações de rede e temporizadores. O TCA modela isso como efeitos que o redutor retorna, mantendo a alteração pura enquanto o trabalho assíncrono é executado separadamente.

Essa separação é o que torna as funcionalidades testáveis.

import ComposableArchitecture

// Reducer stays pure; side effects are values it returns.
// Returning .none means no further work.

Retornando .none

Quando uma ação apenas altera o estado e não precisa de trabalho posterior, o redutor retorna .none. Esse é o efeito mais comum e sinaliza que não há nada assíncrono a fazer.

Você já viu isso; é a base à qual todo ramo recorre.

import ComposableArchitecture

Reduce { state, action in
    switch action {
    case .incrementTapped:
        state.count += 1
        return .none
    }
}

Effect.run para trabalho assíncrono

Para realizar trabalho assíncrono, você retorna .run. O fechamento recebe um identificador send que você usa para enviar resultados de volta ao sistema como novas ações.

Aqui, uma ação fetch carrega dados e envia uma ação de resposta quando termina.

import ComposableArchitecture

Reduce { state, action in
    switch action {
    case .reloadTapped:
        return .run { send in
            let value = try await loadNumber()
            await send(.responseReceived(value))
        }
    case let .responseReceived(value):
        state.count = value
        return .none
    }
}

Enviando resultados de volta como ações

Um efeito não pode alterar o estado diretamente; em vez disso, envia ações por meio de send. O redutor então trata essas ações e atualiza o estado. Esse ciclo mantém cada alteração dentro do redutor.

Assim, uma resposta de rede se torna uma ação de resposta que o redutor armazena.

import ComposableArchitecture

.run { send in
    let result = try await fetch()
    await send(.dataLoaded(result))
}
// .dataLoaded is then handled by the reducer to set state.

Tratando erros em efeitos

O trabalho assíncrono pode lançar uma exceção. Você pode capturar erros dentro do efeito e enviar uma ação de falha, permitindo que o redutor passe para um estado de erro de maneira organizada.

O fechamento catch opcional de .run recebe o erro lançado.

import ComposableArchitecture

.run { send in
    let value = try await loadNumber()
    await send(.responseReceived(value))
} catch: { error, send in
    await send(.loadFailed)
}

Por que as dependências são importantes

Se um redutor chama um cliente de rede ou lê a data diretamente, fica difícil testá-lo e controlá-lo. O TCA resolve isso com dependências: serviços injetáveis que o redutor acessa por meio de @Dependency.

Nos testes, você as substitui por implementações falsas previsíveis.

import ComposableArchitecture

// Instead of calling APIClient.shared directly,
// inject it as a dependency so tests can replace it.

O encapsulador de propriedade @Dependency

Dentro de um redutor, você declara uma dependência com @Dependency, nomeando-a pelo seu caminho de chave nos valores de dependência. A biblioteca fornece itens integrados, como um relógio e um gerador de UUID.

Aqui, o redutor obtém o relógio contínuo.

import ComposableArchitecture

@Reducer
struct TimerFeature {
    @ObservableState
    struct State: Equatable { var seconds = 0 }
    enum Action { case start, tick }

    @Dependency(\.continuousClock) var clock

    var body: some ReducerOf<Self> {
        Reduce { state, action in .none }
    }
}

Usando uma dependência em um efeito

Depois de declarada, você usa a dependência dentro de .run. Como o relógio é injetado, os testes podem fornecer um relógio de teste que avança o tempo sob demanda.

Aqui, o temporizador marca a passagem de um segundo em um segundo usando o relógio injetado.

import ComposableArchitecture

var body: some ReducerOf<Self> {
    Reduce { state, action in
        switch action {
        case .start:
            return .run { send in
                for await _ in self.clock.timer(interval: .seconds(1)) {
                    await send(.tick)
                }
            }
        case .tick:
            state.seconds += 1
            return .none
        }
    }
}

Definindo sua própria dependência

Você registra dependências personalizadas fazendo um valor estar em conformidade com DependencyKey e fornecendo um liveValue. Em seguida, adiciona uma propriedade computada a DependencyValues para que ela receba um caminho de chave.

Aqui, um cliente de API se torna uma dependência de primeira classe.

import ComposableArchitecture

struct NumberClient {
    var fetch: () async throws -> Int
}

extension NumberClient: DependencyKey {
    static let liveValue = NumberClient {
        try await loadNumber()
    }
}

extension DependencyValues {
    var numberClient: NumberClient {
        get { self[NumberClient.self] }
        set { self[NumberClient.self] = newValue }
    }
}

Injetando a dependência personalizada

Com o caminho de chave configurado, o redutor declara o cliente com @Dependency(\.numberClient) e o chama dentro dos efeitos. Em produção, usa-se o valor em produção; nos testes, ele é substituído.

Aqui, uma ação de recarregamento busca dados por meio do cliente injetado.

import ComposableArchitecture

@Reducer
struct NumberFeature {
    @ObservableState
    struct State: Equatable { var value = 0 }
    enum Action { case reload, response(Int) }

    @Dependency(\.numberClient) var numberClient

    var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .reload:
                return .run { send in
                    let n = try await numberClient.fetch()
                    await send(.response(n))
                }
            case let .response(n):
                state.value = n
                return .none
            }
        }
    }
}

Cancelamento de efeitos

Efeitos de longa duração, como temporizadores, devem poder ser cancelados. Você marca um efeito com um identificador cancellable e o cancela mais tarde a partir de outra ação com .cancel.

Aqui, o temporizador pode ser interrompido sob demanda.

import ComposableArchitecture

enum CancelID { case timer }

// Start a cancellable effect:
.run { send in /* ... */ }
    .cancellable(id: CancelID.timer)

// Stop it from another action:
.cancel(id: CancelID.timer)

Verificação rápida: efeitos e resultados

Relembre como um efeito comunica seu resultado de volta ao redutor.

Recapitulação: efeitos e dependências

Você aprendeu como o TCA lida com efeitos colaterais e injeção:

  • Os redutores permanecem puros e retornam valores Effect; .none significa que não há trabalho a fazer.
  • .run executa trabalho assíncrono e devolve results por meio de await send(.action).
  • Os erros são tratados com o fechamento catch do efeito.
  • @Dependency injeta serviços, como o relógio ou seus próprios clientes, declarados por meio de DependencyKey e DependencyValues.
  • Efeitos de longa duração podem se tornar cancellable por identificador.

A seguir, você combinará funcionalidades e as testará exaustivamente.

import ComposableArchitecture

// Recap: pure reducer + Effect.run for async work
// + @Dependency for testable, injectable services.
Grátis para começar

Aprenda Swift com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
122
Aulas
409

Perguntas Frequentes

A aula “Efeitos e Dependências” é grátis?

Sim — o texto completo de “Efeitos e Dependências” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Swift Academy, atualize para CoddyKit PRO. O curso de Swift Academy inclui 4 aulas no total.

O que vou aprender em “Efeitos e Dependências”?

Lide com efeitos colaterais e injete dependências. Você pratica Swift Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Swift Academy?

Nenhuma experiência prévia é necessária. Swift Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Efeitos e Dependências”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Swift Academy?

Sim. Cada aula de Swift Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Estado, Ação e Redutor
  2. A Store e a Integração com SwiftUI
  3. Efeitos e Dependências
  4. Composição e Teste de Funcionalidades
← Voltar para Swift Academy