Efekty i zależności
Obsługa efektów ubocznych i wstrzykiwanie zależności.
Efekty i zależności to bezpłatna lekcja Swift Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Swift Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Swift Academy zawiera 4 lekcji w sumie.
Dlaczego istnieją efekty
Reducery muszą pozostać czyste: otrzymując stan i akcję, modyfikują wyłącznie stan. Jednak rzeczywiste aplikacje potrzebują efektów ubocznych, takich jak żądania sieciowe i timery. TCA modeluje je jako efekty zwracane przez reducer, dzięki czemu mutacje pozostają czyste, a praca asynchroniczna odbywa się osobno.
To rozdzielenie sprawia, że funkcjonalności można testować.
import ComposableArchitecture
// Reducer stays pure; side effects are values it returns.
// Returning .none means no further work.Zwracanie .none
Gdy akcja zmienia tylko stan i nie wymaga dalszej pracy, reducer zwraca .none. Jest to najczęstszy efekt i sygnalizuje, że nie ma nic asynchronicznego do wykonania.
Ten przypadek został już przedstawiony; stanowi podstawę, do której wraca każda gałąź.
import ComposableArchitecture
Reduce { state, action in
switch action {
case .incrementTapped:
state.count += 1
return .none
}
}Effect.run dla pracy asynchronicznej
Aby wykonać pracę asynchroniczną, należy zwrócić .run. Jego domknięcie otrzymuje uchwyt send, za pomocą którego wyniki są przekazywane do systemu jako nowe akcje.
W tym przykładzie akcja pobierania wczytuje dane i po zakończeniu wysyła akcję odpowiedzi.
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
}
}Przekazywanie wyników z powrotem jako akcji
Efekt nie może bezpośrednio modyfikować stanu; zamiast tego wysyła akcje za pośrednictwem send. Następnie reducer obsługuje te akcje i aktualizuje stan. Ta pętla sprawia, że każda mutacja odbywa się wewnątrz reduktora.
Odpowiedź sieciowa staje się więc akcją odpowiedzi, którą reducer zapisuje w stanie.
import ComposableArchitecture
.run { send in
let result = try await fetch()
await send(.dataLoaded(result))
}
// .dataLoaded is then handled by the reducer to set state.Obsługa błędów w efektach
Praca asynchroniczna może zgłaszać błędy. Błędy można przechwycić wewnątrz efektu i wysłać akcję niepowodzenia, pozwalając reducerowi płynnie przejść do stanu błędu.
Opcjonalne domknięcie catch w .run otrzymuje zgłoszony błąd.
import ComposableArchitecture
.run { send in
let value = try await loadNumber()
await send(.responseReceived(value))
} catch: { error, send in
await send(.loadFailed)
}Dlaczego zależności mają znaczenie
Jeśli reducer bezpośrednio wywołuje klienta sieciowego lub odczytuje datę, testowanie i kontrolowanie go staje się trudne. TCA rozwiązuje ten problem za pomocą zależności: usług, które można wstrzykiwać i z których reducer korzysta przez @Dependency.
W testach można zastąpić je przewidywalnymi atrapami.
import ComposableArchitecture
// Instead of calling APIClient.shared directly,
// inject it as a dependency so tests can replace it.Wrapper właściwości @Dependency
Wewnątrz reduktora zależność deklaruje się za pomocą @Dependency, wskazując ją ścieżką klucza w wartościach zależności. Biblioteka udostępnia wbudowane zależności, takie jak zegar i generator uuid.
W tym przykładzie reducer korzysta z zegara ciągłego.
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 }
}
}Używanie zależności w efekcie
Po zadeklarowaniu zależności używa się jej wewnątrz .run. Ponieważ zegar jest wstrzykiwany, testy mogą dostarczyć zegar testowy, który przesuwa czas na żądanie.
W tym przykładzie timer tyka co sekundę, korzystając ze wstrzykniętego zegara.
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
}
}
}Definiowanie własnej zależności
Własne zależności rejestruje się, dostosowując typ do DependencyKey i dostarczając wartość liveValue. Następnie do DependencyValues dodaje się obliczaną właściwość, aby zależność otrzymała ścieżkę klucza.
W tym przykładzie klient API staje się pełnoprawną zależnością.
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 }
}
}Wstrzykiwanie własnej zależności
Mając gotową ścieżkę klucza, reducer deklaruje klienta za pomocą @Dependency(\.numberClient) i wywołuje go wewnątrz efektów. W środowisku produkcyjnym używana jest wartość działająca, a testy ją zastępują.
W tym przykładzie akcja ponownego wczytania pobiera dane za pośrednictwem wstrzykniętego klienta.
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
}
}
}
}Anulowanie efektów
Długotrwałe efekty, takie jak timery, powinny umożliwiać anulowanie. Efekt oznacza się identyfikatorem cancellable, a następnie anuluje z poziomu innej akcji za pomocą .cancel.
W tym przypadku timer można zatrzymać na żądanie.
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)Szybkie sprawdzenie: efekty i wyniki
Przypomnij sobie, jak efekt przekazuje swój wynik z powrotem do reducera.
Podsumowanie: efekty i zależności
Nauczono się, jak TCA obsługuje efekty uboczne i wstrzykiwanie zależności:
- Reducery zachowują czystość i zwracają wartości
Effect;.noneoznacza brak działania. .runwykonuje pracę asynchroniczną i przekazuje wyniki z powrotem przezawait send(.action).- Błędy obsługuje się za pomocą zamknięcia
catchefektu. @Dependencywstrzykuje usługi, takie jak zegar lub własne klienty, zadeklarowane za pomocąDependencyKeyiDependencyValues.- Długotrwałe efekty można oznaczyć jako
cancellableza pomocą identyfikatora.
Następnie funkcje zostaną połączone, a testy będą przeprowadzane wyczerpująco.
import ComposableArchitecture
// Recap: pure reducer + Effect.run for async work
// + @Dependency for testable, injectable services.Często zadawane pytania
Czy lekcja „Efekty i zależności” jest bezpłatna?
Tak — pełny tekst „Efekty i zależności” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Swift Academy, przejdź na CoddyKit PRO. Kurs Swift Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Efekty i zależności”?
Obsługa efektów ubocznych i wstrzykiwanie zależności. Ćwiczysz Swift Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Swift Academy?
Nie wymagamy żadnego doświadczenia. Swift Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Efekty i zależności”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Swift Academy?
Tak. Każda lekcja Swift Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Stan, akcja i reducer
- Store i integracja z SwiftUI
- Efekty i zależności
- Komponowanie i testowanie funkcji