Reduce

// Reduce<State, Action> initializer
  @inlinable
  public init(_ reduce: @escaping (_ state: inout State, _ action: Action) -> Effect<Action>) {
    self.init(internal: reduce)
  }
  • Reduce๋Š” Effect<Action>์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํด๋กœ์ ธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
  • Effect<Action>์„ ์‚ฌ์šฉํ•ด์„œ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌ
    • Reducer ๋‚ด๋ถ€์—์„œ State๋ฅผ ๋ณ€ํ˜•ํ•˜๊ณ  ๊ด€๋ฆฌ
    • Effect๋ฅผ Application์— ํ”ผ๋“œ๋ฐฑ (Side Effect)

-> Reducer ๋‚ด๋ถ€์—์„œ Reduce๋ฅผ ๊ฐ€์ง€๊ณ  ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. ์ด๋•Œ effect๋ฅผ ์ผ์œผ์ผœ ํ”ผ๋“œ๋ฐฑ์„ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๋ฅผ side effect๋ผ๊ณ  ํ•œ๋‹ค.

 

.run

Reduce๋Š” Effect<Action>์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค

  public static func run(
    priority: TaskPriority? = nil,
    operation: @escaping @Sendable (_ send: Send<Action>) async throws -> Void,
    catch handler: (@Sendable (_ error: Error, _ send: Send<Action>) async -> Void)? = nil,
    fileID: StaticString = #fileID,
    line: UInt = #line
  ) -> Self

 

  • Effect์˜ ์ต์Šคํ…์…˜์— ์ •์˜๋œ static ํ•จ์ˆ˜์ธ run์˜ ๋ฐ˜ํ™˜ํƒ€์ž…์€ Self ์ฆ‰ Effect
  • operation์œผ๋กœ ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์‹คํ–‰
operation: @escaping @Sendable (_ send: Send<Action>) async throws -> Void,

 

  • Send<Action>์„ ์‚ฌ์šฉํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ํ”ผ๋“œ๋ฐฑ ํ•˜๋ฉฐ Action ์ผ€์ด์Šค์˜ ์—ฐ๊ด€ ๊ฐ’์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ „๋‹ฌ
  • operation { } ํด๋กœ์ € ๋‚ด๋ถ€์˜ ์ž‘์—…์€ ์ž‘์—…์„ ์œ„ํ•œ ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌ (Task), ๋น„๋™๊ธฐ ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋Š” main ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌ ๋˜์–ด์•ผ ํ•จ(State ๋ฐ˜์˜)

 

Send

operation: @escaping @Sendable (_ send: Send<Action>) async throws -> Void,

 

  • operation { } ํด๋กœ์ € ๋‚ด๋ถ€์— send๋Š” Send<Action>
public struct Send<Action>: Sendable {
  let send: @MainActor @Sendable (Action) -> Void

  public init(send: @escaping @MainActor @Sendable (Action) -> Void) {
    self.send = send
  }
  // ์ƒ๋žต

 

  • Send<Action>์—๋Š” send ์ธ์Šคํ„ด์Šค๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
  • send๋Š” MainActor๋กœ์„œ ๋™์ž‘
    • Effect์˜ ๊ฒฐ๊ณผ๋ฅผ State์— ๋ฐ˜์˜ํ•˜๋ฉด ์ด state๋Š” UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋ฏ€๋กœ  main ์Šค๋ ˆ๋“œ์—์„œ ์ผ์–ด๋‚˜์•ผ ํ•จ
  • Sendable์„ ์ค€์ˆ˜ํ•˜๋Š” ํƒ€์ž…์€ ํ•ด๋‹น ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์Šค๋ ˆ๋“œ ๊ฐ„ ์•ˆ์ „ํ•˜๊ฒŒ ์ „๋‹ฌ ํ•  ์ˆ˜ ์žˆ์Œ
    • Send ๊ตฌ์กฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์„œ Action์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Œ
      case .alert(.presented(.confirmSave)):
        return .run { [transcript = state.transcript] send in
          await send(.delegate(.saveMeeting(transcript: transcript)))
          await self.dismiss()
        }

(์ฝ”๋“œ ์ถœ์ฒ˜ : TCA / episode-code-samples-main / 0249-tca-tour-pt7 )

 

  • ์œ„ ์ฝ”๋“œ์—์„œ await send(.delegate(.saveMeeting(transcript: transcript))๋Š” MainActor ์ธ์Šคํ„ด์Šค์ธ send๊ฐ€ state์˜ ๋ณ€ํ˜•์„ ๋ฉ”์ธ์Šค๋ ˆ๋“œ์—์„œ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ
  • ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋กœ ์–ป์€ ๊ฒฐ๊ณผ ๊ฐ’์„ send๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์•ก์…˜์œผ๋กœ ํ”ผ๋“œ๋ฐฑ ํ•˜๊ณ  main ์Šค๋ ˆ๋“œ์˜ ํ๋ฆ„์œผ๋กœ ๋‹ค์‹œ ํŽธ์ž…์‹œํ‚ค๋Š” ๋ฐฉ์‹

TCA์—์„œ state ๊ฐ’์„ ์ง์ ‘์ ์œผ๋กœ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  send๋ฅผ ํ™œ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ด๋Š” Swift์˜ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ ๊ด€๋ จ์ด ์žˆ๋‹ค. TCA๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์˜ ๋งค์ปค๋‹ˆ์ฆ˜ ์ดํ•ด๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

 

2023.12.20 - [Swift] - [Swift] ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ(async/await, actor, sendable)

MainActor, Sendable์— ๋Œ€ํ•œ ์˜ˆ์‹œ ์ฝ”๋“œ์™€ ๋™์ž‘์€ ์ด ๊ธ€ ์ฐธ๊ณ 

 

์•„ task ์•ˆ์—์„œ ๊ฐ’์„ ๋ณ€๊ฒฝ์„ ๋ชปํ•˜๋‹ˆ๊นŒ effect์˜ run ํ•จ์ˆ˜์— send๋ฅผ ์‚ฌ์šฉํ•ด์„œ action์œผ๋กœ ํ”ผ๋“œ๋ฐฑ ํ•˜๋Š” ๊ตฌ๋‚˜

 

 

๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋งค์ปค๋‹ˆ์ฆ˜ ์ดํ•ดํ•˜๊ธฐ

 

  • ์œ„ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์—์„œ Task๊ฐ€ ์ƒ์„ฑํ•  ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ์—์„œ ์ƒˆ๋กœ์šด ๊ฐ’์„ ํ• ๋‹นํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์ด ํ• ๋‹น ๋˜๋Š” ์‹œ์ ์€ ์–ด๋Š ์‹œ์ ์— ์ด๋ฃจ์–ด์งˆ์ง€ ํ™•์ •ํ•  ์ˆ˜ ์—†์Œ
  • ์™ธ๋ถ€ ๋ณ€์ˆ˜๋ฅผ ๋น„๋™๊ธฐ ๋งฅ๋ฝ์—์„œ ๋ณ€ํ˜•ํ•  ์ˆ˜ ์—†์Œ

 

  • inout์€ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ๋œ ๊ฐ’์˜ ์›๋ณธ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ
  • ๋ณ€ํ˜• ๊ฐ€๋Šฅ์„ฑ์„ ๊ฐ€์ง€๋Š” inout ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋น„๋™๊ธฐ ๋งฅ๋ฝ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ

-> ๊ฐ’์˜ ๋ณ€ํ˜•์€ main์—์„œ ์ˆ˜ํ–‰๋˜์–ด์•ผ ํ•จ -> TCA๊ฐ€ MainActor, Send๋ฅผ ๋„์ž…ํ•œ ์ด์œ 

 

  • Task์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ณ€ํ˜•์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ์ „๋‹ฌ๋˜์–ด์•ผ ํ•จ.
  • ๊ฐ’์˜ ์บก์ฒ˜ ๋ฐฉ์‹์œผ๋กœ ์ „๋‹ฌ
class AsyncTest {
    var foo = ""
    
    func doAsyncBar(
        _ foo: inout String,
        mainCompletion: @escaping @Sendable @MainActor (String) -> Void) async {
            Task { [foo = foo] in
                let taskResult = "Task" + foo
                await mainCompletion(taskResult)
            }
    }
}

 

  • ๊ฐ’์„ ์บก์ณํ•ด์„œ ์ „๋‹ฌ ๋ฐ›๊ณ  main์œผ๋กœ ํ”ผ๋“œ๋ฐฑ ํ•˜๊ธฐ ์œ„ํ•ด mainCompletion ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€
  • mainCompletion
    • mainCompletion์€ @MainActor์„ ์‚ฌ์šฉํ•ด์„œ main ์Šค๋ ˆ๋“œ์—์„œ์˜ ์ž‘์—…์„ ๋ณด์žฅ
    • @Sendable๋กœ ์Šค๋ ˆ๋“œ ์•ˆ์ „ํ•˜๊ฒŒ ๊ฐ’์„ ์ „๋‹ฌ
    • Task์˜ ๊ฒฐ๊ณผ๋ฅผ ํด๋กœ์ €๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ ํ•  ์ˆ˜ ์žˆ์Œ
final class AsyncTest: @unchecked Sendable {
    private var foo: String
    
    init(foo: String = "") {
        self.foo = foo
    }
    
    func doAsyncBar(
        _ foo: inout String,
        mainCompletion: @escaping @Sendable @MainActor (String) -> Void) async {
            Task { [foo = foo] in
                let taskResult = "Task" + foo
                await mainCompletion(taskResult)
            }
    }
    
    func updateUI() async {
        await doAsyncBar(&foo) { result in
            self.foo = result
        }
    }
}

 

์ตœ์ข…ํ˜•ํƒœ๋ฅผ ํ™•์ธํ•ด ๋ณด๋ฉด TCA์˜ Effect run๊ณผ ์œ ์‚ฌํ•œ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

operation: @escaping @Sendable (_ send: Send<Action>) async throws -> Void,

 

  let send: @MainActor @Sendable (Action) -> Void
  
  public init(send: @escaping @MainActor @Sendable (Action) -> Void) {
    self.send = send
  }

 

์ฐธ๊ณ  ๋ฐ ์ถœ์ฒ˜

 

Chapter 6. Swift์˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์™€ TCA์—์„œ์˜ ์‘์šฉ | Built with Notion

์ด๋ฒˆ ์žฅ์—์„œ๋Š” TCA์—์„œ์˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ธฐ ์ „์— Swift Concurrency, ์ฆ‰ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Combine ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋‹ฌ๋ฆฌ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ Task์˜ ๋ณ‘

axiomatic-fuschia-666.notion.site

https://pixabay.com/ko/photos/%EB%B9%84%EB%88%84-%EC%A7%91%EC%97%90%EC%84%9C-%EB%A7%8C%EB%93%A0-%EB%B9%84%EB%88%84-8429699/

 

'iOS ๐ŸŽ > Architecture' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[TCA] Overriding Dependencies (feat. Dependency Injection)  (0) 2023.12.13
[SwiftUI/TCA] Binding  (0) 2023.12.07
[SwiftUI/TCA] Scope  (1) 2023.11.30
[SwiftUI/TCA] Store and ViewStore  (0) 2023.11.29
[SwiftUI/TCA] Effect ๊ตฌํ˜„๊ณผ ํ™œ์šฉ  (0) 2023.11.29