เอฟเฟกต์และการพึ่งพา
จัดการผลข้างเคียงและฉีดการพึ่งพา
เอฟเฟกต์และการพึ่งพา เป็นบทเรียน Swift Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Swift Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงมีเอฟเฟกต์
รีดิวเซอร์ต้องบริสุทธิ์ กล่าวคือ เมื่อได้รับสถานะและการกระทำ รีดิวเซอร์จะเปลี่ยนสถานะเท่านั้น แต่แอปจริงจำเป็นต้องมีผลข้างเคียง เช่น คำขอเครือข่ายและตัวจับเวลา TCA จำลองสิ่งเหล่านี้เป็น เอฟเฟกต์ ที่รีดิวเซอร์ส่งคืน โดยยังคงให้การเปลี่ยนสถานะบริสุทธิ์ ขณะที่งานแบบอะซิงโครนัสทำงานแยกกัน
การแยกส่วนนี้ทำให้ฟีเจอร์ทดสอบได้
import ComposableArchitecture
// Reducer stays pure; side effects are values it returns.
// Returning .none means no further work.การส่งคืน .none
เมื่อการกระทำเปลี่ยนสถานะเพียงอย่างเดียวและไม่ต้องทำงานต่อ รีดิวเซอร์จะส่งคืน .none นี่คือเอฟเฟกต์ที่ใช้บ่อยที่สุด และเป็นสัญญาณว่าไม่มีงานแบบอะซิงโครนัสที่ต้องทำ
คุณได้เห็นสิ่งนี้มาแล้ว โดยเป็นค่าพื้นฐานที่ทุกแขนงใช้เมื่อไม่มีกรณีอื่น
import ComposableArchitecture
Reduce { state, action in
switch action {
case .incrementTapped:
state.count += 1
return .none
}
}Effect.run สำหรับงานแบบอะซิงโครนัส
หากต้องการทำงานแบบอะซิงโครนัส ให้ส่งคืน .run โคลเชอร์ของค่านี้จะรับตัวจัดการ send ซึ่งคุณใช้ส่งผลลัพธ์กลับเข้าสู่ระบบในรูปการกระทำใหม่
ในที่นี้ การกระทำดึงข้อมูลจะโหลดข้อมูลและส่งการกระทำตอบกลับเมื่อเสร็จสิ้น
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
}
}การส่งผลลัพธ์กลับเป็นการกระทำ
เอฟเฟกต์ไม่สามารถเปลี่ยนสถานะโดยตรงได้ แต่จะส่งการกระทำผ่าน send จากนั้นรีดิวเซอร์จะจัดการการกระทำเหล่านั้นและอัปเดตสถานะ วงจรนี้ทำให้การเปลี่ยนแปลงทุกอย่างอยู่ภายในรีดิวเซอร์
ดังนั้น การตอบกลับจากเครือข่ายจึงกลายเป็นการกระทำตอบกลับที่รีดิวเซอร์จัดเก็บไว้
import ComposableArchitecture
.run { send in
let result = try await fetch()
await send(.dataLoaded(result))
}
// .dataLoaded is then handled by the reducer to set state.การจัดการข้อผิดพลาดในเอฟเฟกต์
งานแบบอะซิงโครนัสอาจโยนข้อผิดพลาดได้ คุณสามารถดักจับข้อผิดพลาดภายในเอฟเฟกต์และส่งการกระทำแจ้งความล้มเหลว เพื่อให้รีดิวเซอร์เปลี่ยนเข้าสู่สถานะข้อผิดพลาดได้อย่างเป็นระเบียบ
โคลเชอร์ catch ที่เป็นทางเลือกของ .run จะรับข้อผิดพลาดที่ถูกโยนออกมา
import ComposableArchitecture
.run { send in
let value = try await loadNumber()
await send(.responseReceived(value))
} catch: { error, send in
await send(.loadFailed)
}เหตุใดการพึ่งพาจึงสำคัญ
หากรีดิวเซอร์เรียกไคลเอ็นต์เครือข่ายหรืออ่านวันที่โดยตรง รีดิวเซอร์จะทดสอบและควบคุมได้ยาก TCA แก้ปัญหานี้ด้วยการพึ่งพา ซึ่งเป็นบริการที่ฉีดเข้าได้และรีดิวเซอร์เข้าถึงผ่าน @Dependency
ในการทดสอบ คุณสามารถสลับสิ่งเหล่านี้เป็นตัวจำลองที่ให้ผลลัพธ์คาดเดาได้
import ComposableArchitecture
// Instead of calling APIClient.shared directly,
// inject it as a dependency so tests can replace it.ตัวห่อคุณสมบัติ @Dependency
ภายในรีดิวเซอร์ คุณประกาศการพึ่งพาด้วย @Dependency โดยตั้งชื่อผ่านเส้นทางคีย์ในค่าการพึ่งพา ไลบรารีมีการพึ่งพาที่มีมาให้ เช่น clock และตัวสร้างรหัสเฉพาะสากล
ในที่นี้ รีดิวเซอร์ดึง continuous clock มาใช้
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 }
}
}การใช้การพึ่งพาในเอฟเฟกต์
เมื่อประกาศแล้ว คุณจะใช้การพึ่งพาภายใน .run เนื่องจาก clock ถูกฉีดเข้ามา การทดสอบจึงสามารถจัดหานาฬิกาสำหรับทดสอบที่เลื่อนเวลาได้ตามต้องการ
ในที่นี้ timer จะทำงานทุกวินาทีโดยใช้นาฬิกาที่ฉีดเข้ามา
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
}
}
}การกำหนดการพึ่งพาของคุณเอง
คุณลงทะเบียนการพึ่งพาแบบกำหนดเองได้โดยทำให้ค่าใดค่าหนึ่งสอดคล้องกับ DependencyKey และกำหนดค่า liveValue จากนั้นเพิ่มคุณสมบัติที่คำนวณค่าให้กับ DependencyValues เพื่อให้การพึ่งพานั้นมีเส้นทางคีย์
ในที่นี้ ไคลเอ็นต์ของส่วนติดต่อโปรแกรมประยุกต์กลายเป็นการพึ่งพาระดับพื้นฐาน
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 }
}
}การฉีดการพึ่งพาแบบกำหนดเอง
เมื่อมีเส้นทางคีย์แล้ว รีดิวเซอร์จะประกาศไคลเอ็นต์ด้วย @Dependency(\.numberClient) และเรียกใช้ไคลเอ็นต์ภายในเอฟเฟกต์ สภาพแวดล้อมจริงจะใช้ค่าที่ทำงานจริง ส่วนการทดสอบจะเขียนทับค่านั้น
ในที่นี้ การกระทำโหลดใหม่จะดึงข้อมูลผ่านไคลเอ็นต์ที่ฉีดเข้ามา
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
}
}
}
}การยกเลิกเอฟเฟกต์
เอฟเฟกต์ที่ทำงานเป็นเวลานาน เช่น ตัวจับเวลา ควรยกเลิกได้ คุณติดป้ายเอฟเฟกต์ด้วยรหัส cancellable แล้วจึงยกเลิกในภายหลังจากแอ็กชันอื่นด้วย .cancel.
ในที่นี้ คุณสามารถหยุดตัวจับเวลาได้ตามต้องการ.
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)ตรวจสอบอย่างรวดเร็ว: เอฟเฟกต์และผลลัพธ์
ทบทวนว่าเอฟเฟกต์ส่งผลลัพธ์กลับไปยังรีดิวเซอร์อย่างไร.
สรุป: เอฟเฟกต์และการพึ่งพา
คุณได้เรียนรู้วิธีที่ TCA จัดการผลข้างเคียงและการแทรกการพึ่งพา:
- รีดิวเซอร์ยังคงบริสุทธิ์และคืนค่า
Effectโดย.noneหมายถึงไม่มีงานที่ต้องทำ .runทำงานแบบอะซิงโครนัสและส่งผลลัพธ์กลับผ่านawait send(.action)- ข้อผิดพลาดจัดการได้ด้วยโคลเชอร์
catchของเอฟเฟกต์ @Dependencyใช้แทรกบริการต่าง ๆ เช่น นาฬิกาหรือไคลเอนต์ของคุณเอง ซึ่งประกาศผ่านDependencyKeyและDependencyValues- เอฟเฟกต์ที่ทำงานเป็นเวลานานสามารถทำให้ยกเลิกได้ด้วยรหัส
cancellable
ถัดไป คุณจะประกอบฟีเจอร์ต่าง ๆ เข้าด้วยกันและทดสอบอย่างครบถ้วน.
import ComposableArchitecture
// Recap: pure reducer + Effect.run for async work
// + @Dependency for testable, injectable services.คำถามที่พบบ่อย
บทเรียน “เอฟเฟกต์และการพึ่งพา” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เอฟเฟกต์และการพึ่งพา” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Swift Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “เอฟเฟกต์และการพึ่งพา”
จัดการผลข้างเคียงและฉีดการพึ่งพา คุณปฏิบัติ Swift Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Swift Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Swift Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “เอฟเฟกต์และการพึ่งพา” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Swift Academy นี้ได้ไหม
ได้ บทเรียน Swift Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- สถานะ แอ็กชัน และรีดิวเซอร์
- Store และการผสานกับ SwiftUI
- เอฟเฟกต์และการพึ่งพา
- การประกอบและทดสอบความสามารถ