0Pricing
Swift Academy · บทเรียน

เอฟเฟกต์และการพึ่งพา

จัดการผลข้างเคียงและฉีดการพึ่งพา

เอฟเฟกต์และการพึ่งพา เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. สถานะ แอ็กชัน และรีดิวเซอร์
  2. Store และการผสานกับ SwiftUI
  3. เอฟเฟกต์และการพึ่งพา
  4. การประกอบและทดสอบความสามารถ
← กลับไปที่ Swift Academy