Swift Academy · Les

Effects en dependencies

Verwerk side effects en injecteer dependencies.

Les 3 van 413 stappen

Effects en dependencies is een gratis Swift Academy-les op CoddyKit. Dit is les 3 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Swift Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Swift Academy bevat in totaal 4 lessen.

Waarom Effects bestaan

Reducers moeten puur blijven: gegeven een state en een action wijzigen ze alleen state. Maar echte apps hebben bijwerkingen nodig, zoals netwerkaanvragen en timers. TCA modelleert deze als Effects die de reducer retourneert, zodat de mutatie puur blijft terwijl het asynchrone werk apart wordt uitgevoerd.

Dankzij deze scheiding zijn features goed te testen.

import ComposableArchitecture

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

.none retourneren

Wanneer een action alleen state wijzigt en geen vervolgwerk nodig heeft, retourneert de reducer .none. Dit is het meest voorkomende effect en geeft aan dat er niets asynchroon hoeft te gebeuren.

Je hebt dit al gezien; het is de basis waarop elke tak terugvalt.

import ComposableArchitecture

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

Effect.run voor asynchroon werk

Om asynchroon werk uit te voeren, retourneer je .run. De closure ontvangt een send-handvat waarmee je resultaten als nieuwe actions terug het systeem in stuurt.

Hier laadt een fetch-action gegevens en stuurt deze na afloop een response-action.

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
    }
}

Resultaten als Actions terugsturen

Een effect kan state niet rechtstreeks wijzigen; in plaats daarvan stuurt het actions via send. De reducer handelt die actions vervolgens af en werkt state bij. Dankzij deze lus vinden alle mutaties binnen de reducer plaats.

Zo wordt een netwerkreactie een response-action die de reducer opslaat.

import ComposableArchitecture

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

Fouten in Effects afhandelen

Asynchroon werk kan fouten veroorzaken. Je kunt fouten in het effect opvangen en een failure-action sturen, zodat de reducer netjes naar een foutstatus kan gaan.

De optionele closure catch op .run ontvangt de veroorzaakte fout.

import ComposableArchitecture

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

Waarom afhankelijkheden belangrijk zijn

Als een reducer een netwerkclient aanroept of de datum rechtstreeks leest, wordt deze moeilijk te testen en te beheersen. TCA lost dit op met afhankelijkheden: injecteerbare diensten die je reducer benadert via @Dependency.

Bij het testen vervang je ze door voorspelbare testdubbels.

import ComposableArchitecture

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

De property-wrapper @Dependency

Binnen een reducer declareer je een afhankelijkheid met @Dependency en benoem je deze via het key path in de afhankelijkheidswaarden. De bibliotheek levert ingebouwde afhankelijkheden, zoals een klok en een UUID-generator.

Hier haalt de reducer de doorlopende klok op.

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 }
    }
}

Een afhankelijkheid in een effect gebruiken

Zodra de afhankelijkheid is gedeclareerd, gebruik je deze binnen .run. Omdat de klok is geïnjecteerd, kunnen testklokken tijdens het testen op aanvraag tijd vooruitzetten.

Hier tikt de timer elke seconde met behulp van de geïnjecteerde klok.

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
        }
    }
}

Je eigen afhankelijkheid definiëren

Je registreert aangepaste afhankelijkheden door een waarde te laten voldoen aan DependencyKey en een liveValue te leveren. Vervolgens voeg je een berekende eigenschap toe aan DependencyValues, zodat deze een key path krijgt.

Hier wordt een API-client een volwaardige afhankelijkheid.

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 }
    }
}

De aangepaste afhankelijkheid injecteren

Met het key path op zijn plaats declareert de reducer de client met @Dependency(\.numberClient) en roept deze aan binnen effecten. In productie wordt de live value gebruikt; bij het testen overschrijf je deze.

Hier haalt een herlaadactie gegevens op via de geïnjecteerde client.

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
            }
        }
    }
}

Effecten annuleren

Langlopende effecten, zoals timers, moeten kunnen worden geannuleerd. Je voorziet een effect van een cancellable-id en annuleert het later vanuit een andere actie met .cancel.

Hier kun je de timer op verzoek stoppen.

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)

Snelle controle: effecten en resultaten

Haal je voor de geest hoe een effect zijn resultaat terugstuurt naar de reducer.

Samenvatting: effecten en afhankelijkheden

Je hebt geleerd hoe TCA neveneffecten en injectie afhandelt:

  • Reducers blijven puur en retourneren Effect-waarden; .none betekent dat er niets wordt uitgevoerd.
  • .run voert asynchroon werk uit en stuurt resultaten terug via await send(.action).
  • Fouten worden afgehandeld met de catch-closure van het effect.
  • @Dependency injecteert diensten zoals de klok of je eigen clients, die worden gedeclareerd via DependencyKey en DependencyValues.
  • Langlopende effecten kunnen op basis van een id cancellable worden gemaakt.

Vervolgens combineer je onderdelen en test je ze volledig.

import ComposableArchitecture

// Recap: pure reducer + Effect.run for async work
// + @Dependency for testable, injectable services.
Gratis beginnen

Leer Swift met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
122
Lessen
409

Veelgestelde vragen

Is de les “Effects en dependencies” gratis?

Ja — je kunt hier op het web alle 3 lessen van het leerpad Swift Academy, waaronder “Effects en dependencies”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Swift Academy bevat in totaal 4 lessen.

Wat leer ik in “Effects en dependencies”?

Verwerk side effects en injecteer dependencies. Je oefent met Swift Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Swift Academy te beginnen?

Ervaring vooraf is niet nodig. Swift Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.

Hoe lang duurt de les “Effects en dependencies”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Swift Academy?

Ja. Elke les over Swift Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. State, action en reducer
  2. De Store en SwiftUI-integratie
  3. Effects en dependencies
  4. Features samenstellen en testen
← Terug naar Swift Academy