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

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

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