0Pricing
Swift Academy · Leçon

Effets et dépendances

Gérez les effets secondaires et injectez les dépendances.

Effets et dépendances est une leçon Swift Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Swift Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Swift Academy comprend 4 leçons au total.

Pourquoi les effets existent

Les réducteurs doivent rester purs : à partir d’un état et d’une action, ils ne modifient que l’état. Mais les applications réelles ont besoin d’effets de bord, comme des requêtes réseau et des minuteurs. TCA les modélise sous forme d’effets renvoyés par le réducteur, ce qui préserve la pureté des modifications tandis que le travail asynchrone s’exécute séparément.

C’est cette séparation qui rend les fonctionnalités faciles à vérifier.

import ComposableArchitecture

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

Renvoyer .none

Lorsqu’une action modifie uniquement l’état et ne nécessite aucun travail supplémentaire, le réducteur renvoie .none. C’est l’effet le plus courant et il indique qu’aucune tâche asynchrone ne doit être effectuée.

Vous l’avez déjà vu ; c’est la valeur de repli sur laquelle revient chaque branche.

import ComposableArchitecture

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

Effect.run pour le travail asynchrone

Pour effectuer un travail asynchrone, vous renvoyez .run. Sa fermeture reçoit une référence send que vous utilisez pour retransmettre les résultats au système sous forme de nouvelles actions.

Ici, une action fetch charge les données et envoie une action de réponse une fois l’opération terminée.

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

Retransmettre les résultats sous forme d’actions

Un effet ne peut pas modifier directement l’état ; il envoie plutôt des actions par l’intermédiaire de send. Le réducteur traite ensuite ces actions et met l’état à jour. Cette boucle maintient toutes les modifications à l’intérieur du réducteur.

Ainsi, une réponse réseau devient une action de réponse que le réducteur enregistre.

import ComposableArchitecture

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

Gérer les erreurs dans les effets

Le travail asynchrone peut lever une erreur. Vous pouvez intercepter les erreurs dans l’effet et envoyer une action d’échec, afin que le réducteur passe proprement à un état d’erreur.

La fermeture facultative catch de .run reçoit l’erreur levée.

import ComposableArchitecture

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

Pourquoi les dépendances sont importantes

Si un réducteur appelle directement un client réseau ou lit directement la date, il devient difficile à vérifier et à contrôler. TCA résout ce problème avec des dépendances : des services injectables auxquels votre réducteur accède via @Dependency.

Dans les vérifications, vous les remplacez par des versions simulées et prévisibles.

import ComposableArchitecture

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

Le modificateur de propriété @Dependency

Dans un réducteur, vous déclarez une dépendance avec @Dependency en la désignant par son chemin de clé dans les valeurs des dépendances. La bibliothèque fournit des éléments intégrés comme clock et un générateur d’UUID.

Ici, le réducteur utilise l’horloge continue.

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

Utiliser une dépendance dans un effet

Une fois déclarée, vous utilisez la dépendance dans .run. Comme clock est injecté, les vérifications peuvent fournir un timer de test qui fait avancer le temps à la demande.

Ici, le timer émet une impulsion chaque seconde en utilisant clock injecté.

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

Définir votre propre dépendance

Vous enregistrez des dépendances personnalisées en rendant une valeur conforme à DependencyKey et en fournissant un liveValue. Vous ajoutez ensuite une propriété calculée à DependencyValues afin qu’elle obtienne un chemin de clé.

Ici, un client d’API devient une dépendance de premier ordre.

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

Injecter la dépendance personnalisée

Une fois le chemin de clé en place, le réducteur déclare le client avec @Dependency(\.numberClient) et l’appelle dans les effets. En production, la valeur active est utilisée ; dans les vérifications, vous la remplacez.

Ici, une action de rechargement récupère les données par l’intermédiaire du client injecté.

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

Annulation des effets

Les effets de longue durée, comme les minuteurs, devraient pouvoir être annulés. Vous associez à un effet un identifiant cancellable, puis vous l’annulez plus tard depuis une autre action avec .cancel.

Ici, le minuteur peut être arrêté à la demande.

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)

Vérification rapide : effets et résultats

Rappelez-vous comment un effet renvoie son résultat au réducteur.

Récapitulatif : effets et dépendances

Vous avez appris comment TCA gère les effets secondaires et l’injection :

  • Les réducteurs restent purs et renvoient des valeurs Effect ; .none signifie qu’aucun travail n’est effectué.
  • .run exécute un travail asynchrone et renvoie les résultats via await send(.action).
  • Les erreurs sont gérées avec la fermeture catch de l’effet.
  • @Dependency injecte des services comme l’horloge ou vos propres clients, déclarés via DependencyKey et DependencyValues.
  • Les effets de longue durée peuvent être rendus cancellable à l’aide d’un identifiant.

Vous allez maintenant composer des fonctionnalités et les tester de manière exhaustive.

import ComposableArchitecture

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

Questions Fréquemment Posées

La leçon « Effets et dépendances » est-elle gratuite ?

Oui — le texte complet de « Effets et dépendances » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Swift Academy, passe à CoddyKit PRO. Le cours Swift Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Effets et dépendances » ?

Gérez les effets secondaires et injectez les dépendances. Tu pratiques Swift Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Swift Academy ?

Aucune expérience préalable n'est requise. Swift Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Effets et dépendances » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Swift Academy ?

Oui. Chaque leçon Swift Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. État, action et réducteur
  2. Le Store et l’intégration à SwiftUI
  3. Effets et dépendances
  4. Composer et tester des fonctionnalités
← Retour à Swift Academy