0Pricing
Swift Academy · Lektion

Effects und Dependencies

Behandeln Sie Seiteneffekte und injizieren Sie Abhängigkeiten.

Effects und Dependencies ist eine kostenlose Swift Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Swift Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Swift Academy-Kurs umfasst insgesamt 4 Lektionen.

Warum es Effects gibt

Reducer müssen rein bleiben: Aus einem State und einer Action verändern sie ausschließlich den State. Echte Apps benötigen jedoch Seiteneffekte wie Netzwerkanfragen und Timer. TCA modelliert diese als Effects, die der Reducer zurückgibt. So bleibt die Mutation rein, während die asynchrone Arbeit getrennt ausgeführt wird.

Diese Trennung macht Features testbar.

import ComposableArchitecture

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

.none zurückgeben

Wenn eine Action nur den State ändert und keine anschließende Arbeit benötigt, gibt der Reducer .none zurück. Dies ist der häufigste Effect und signalisiert, dass keine asynchrone Arbeit ausgeführt werden muss.

Sie haben dies bereits gesehen. Jeder Zweig verwendet dies als Standardrückgabe.

import ComposableArchitecture

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

Effect.run für asynchrone Arbeit

Um asynchrone Arbeit auszuführen, geben Sie .run zurück. Die Closure erhält einen send-Handle, mit dem Sie Ergebnisse als neue Actions in das System zurückgeben.

Hier lädt eine Fetch-Action Daten und sendet nach Abschluss eine 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
    }
}

Ergebnisse als Actions zurückgeben

Ein Effect kann den State nicht direkt verändern, sondern sendet stattdessen Actions über send. Der Reducer verarbeitet diese Actions anschließend und aktualisiert den State. So bleibt jede Mutation innerhalb des Reducers.

Eine Netzwerkantwort wird dadurch zu einer Response-Action, die der Reducer speichert.

import ComposableArchitecture

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

Fehler in Effects behandeln

Asynchrone Arbeit kann Fehler werfen. Sie können Fehler innerhalb des Effects abfangen und eine Failure-Action senden, sodass der Reducer sauber in einen Fehlerzustand wechseln kann.

Die optionale catch-Closure von .run erhält den geworfenen Fehler.

import ComposableArchitecture

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

Warum Dependencies wichtig sind

Wenn ein Reducer direkt einen Netzwerk-Client aufruft oder das Datum ausliest, wird er schwer testbar und kontrollierbar. TCA löst dies mit Dependencies: injizierbaren Diensten, auf die Ihr Reducer über @Dependency zugreift.

In Tests ersetzen Sie sie durch vorhersehbare Fakes.

import ComposableArchitecture

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

Der @Dependency-Property-Wrapper

Innerhalb eines Reducers deklarieren Sie eine Dependency mit @Dependency und benennen sie über ihren Key Path in den Dependency-Werten. Die Bibliothek stellt integrierte Dependencies bereit, etwa eine Uhr und einen UUID-Generator.

Hier bindet der Reducer die kontinuierliche Uhr ein.

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

Eine Dependency in einem Effect verwenden

Nach der Deklaration verwenden Sie die Dependency innerhalb von .run. Da die Uhr injiziert wird, können Tests eine Testuhr bereitstellen, die die Zeit bei Bedarf voranschreiten lässt.

Hier wird der Timer mithilfe der injizierten Uhr jede Sekunde ausgelöst.

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

Eine eigene Dependency definieren

Sie registrieren eigene Dependencies, indem Sie einen Wert DependencyKey entsprechen lassen und einen liveValue bereitstellen. Anschließend fügen Sie DependencyValues eine berechnete Eigenschaft hinzu, damit ein Key Path verfügbar ist.

Hier wird ein API-Client zu einer vollwertigen Dependency.

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

Die eigene Dependency injizieren

Mit dem eingerichteten Key Path deklariert der Reducer den Client mit @Dependency(\.numberClient) und ruft ihn innerhalb von Effects auf. In der Produktion wird der Live-Wert verwendet, während Tests ihn überschreiben.

Hier lädt eine Reload-Action Daten über den injizierten 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
            }
        }
    }
}

Effekte abbrechen

Lang laufende Effekte wie Timer sollten abbrechbar sein. Sie versehen einen Effekt mit einer cancellable-ID und brechen ihn später aus einer anderen Aktion mit .cancel ab.

Hier kann der Timer bei Bedarf gestoppt werden.

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)

Kurztest: Effekte und Ergebnisse

Rufen Sie sich ins Gedächtnis, wie ein Effekt sein Ergebnis an den Reducer zurückmeldet.

Zusammenfassung: Effekte und Abhängigkeiten

Sie haben gelernt, wie TCA Seiteneffekte und Dependency Injection handhabt:

  • Reducer bleiben rein und liefern Effect-Werte; .none bedeutet, dass keine Arbeit anfällt.
  • .run führt asynchrone Arbeit aus und leitet Ergebnisse über await send(.action) zurück.
  • Fehler werden in der catch-Closure des Effekts behandelt.
  • @Dependency injiziert Services wie die Uhr oder eigene Clients, die über DependencyKey und DependencyValues deklariert werden.
  • Lang laufende Effekte können anhand einer ID als cancellable ausgeführt werden.

Als Nächstes setzen Sie Features zusammen und testen sie umfassend.

import ComposableArchitecture

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

Häufig gestellte Fragen

Ist die Lektion „Effects und Dependencies“ kostenlos?

Ja — der vollständige Text von „Effects und Dependencies“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Swift Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Swift Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Effects und Dependencies“?

Behandeln Sie Seiteneffekte und injizieren Sie Abhängigkeiten. Du übst Swift Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Swift Academy zu starten?

Keine Vorkenntnisse erforderlich. Swift Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Effects und Dependencies“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Swift Academy-Lektion Code schreiben und ausführen?

Ja. Jede Swift Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. State, Action und Reducer
  2. Der Store und die SwiftUI-Integration
  3. Effects und Dependencies
  4. Features kombinieren und testen
← Zurück zu Swift Academy