์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
- realm
- UIKit
- combine
- reactorkit
- Swfit
- designpattern
- raywenderlich
- swift
- ios
- BFS
- visionOS
- arkit
- tableView
- XCTest
- node.js
- BOJ
- ํ๋ก๊ทธ๋๋จธ์ค
- Lv2
- TCA
- rxcocoa
- SwiftUI
- SnapKit
- Xcode
- Flutter
- ํจ์คํธ์บ ํผ์ค
- CollectionView
- RxSwift
- ๋ฐฑ์ค
- MVVM
- Kuring
- Today
- Total
lgvv98
[TCA] ๊ณต๋ถ๊ธฐ๋ก #1 (ReducerProtocol) ๋ณธ๋ฌธ
#### TCA ๊ณต๋ถ์์
(์ฒซ ํฌ์คํ
๋ ์ง) 2023. 1. 16. 18:31
(์
๋ฐ์ดํธ) 2023. 10. 08. 01:08
- ์
๋ฐ์ดํธ ์ฌ์ : TCA 1.0.0 ์ถ์๋ก ์ธํ ๊ณต๋ถ ๊ณํ ์
๋ฐ์ดํธ. ํด๋น ํฌ์คํ
์ ํ์ฌ ํฌ๊ฒ ์๋ฏธ๊ฐ ์๋ ์ํ
- ํ๊ฒฝ
- Xcode 15.0
- TCA 1.0.0
- TCA ๊ณต๋ถ๋ ์๋ ๋งํฌ์์ ํ์ธ ๊ฐ๋ฅ
https://rldd.tistory.com/category/apple/%F0%9F%A6%A5%20TCA
'apple/๐ฆฅ TCA' ์นดํ ๊ณ ๋ฆฌ์ ๊ธ ๋ชฉ๋ก
rldd.tistory.com
https://github.com/pointfreeco/swift-composable-architecture
GitHub - pointfreeco/swift-composable-architecture: A library for building applications in a consistent and understandable way,
A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind. - GitHub - pointfreeco/swift-composable-architecture: A library for bu...
github.com
#### ํ๊ฒฝ
- XCode: 14.0
- swift-composable-architecture ~> main
#### ์ด๋ป๊ฒ ํ์ตํ ๊ฒ์ธ๊ฐ?
- TCA์ Tic-Tac-Toc Example์ ๋ถ์ํด๋ณด๋ฉด ํ ์ด ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๊ธฐ.
### ๋ชฉํ
- TCA์ ๋ง์ ๋ถ๋ถ์ ์ฌ์ฉํด๋ณด๊ธฐ
### ์์ ํ ๊ฐ์ธ์ ์ธ ์ ๋ฆฌ
- IfLetStore: nilํ์ ์์ ์ฌ์ฉ ๊ฐ๋ฅํ์ฌ nil ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ SwiftUI์ View๋ฅผ ๋ ๊น๋ํ๊ฒ์ฒ๋ฆฌ ๊ฐ๋ฅํด ๋ณด์.
- Scope: onAppear๊ฐ ์ค๋ณต ํธ์ถ๋๋ ์ด์๊ฐ ์์๋๋ฐ, Scope๋ฅผ ํตํด ๋ฒ์๋ฅผ ์ง์ ํ๊ณ ๋๋ ์ ์์.
- View(Action, State), Core(Action, State)๋ ๋ ๋ฆฝ์ ์ผ๋ก ์กด์ฌํ๋ ๊ฒ์ด ์ข๋ค๊ณ ํ๋จ.
: ReactorKit์ ์๊ฐํ๋ฉด ๋์ผํ๊ฒ ์๋๊น๋ฅผ ๊ณ ๋ฏผํ์ผ๋, ๋์ด ๋๋จ์ผ๋ก์จ Core์ Action์ด ๋ ๋ช ํํ๊ณ ๊น๋ํด์ง ๊ธฐ๋ถ์ด ๋ค์์.
: ์ด ๋ฐฉ์์ด ์ ๋ง ์ข๋ค๊ณ ๋๋ผ๋๋ฐ, ์ฝ๋ ๊ด๋ฆฌ๊ฐ ๋ ์ ๋๋ ๊ธฐ๋ถ์ด๋ค!
- TCA์ ViewModel ๋์ ์ Core๋ผ๋ ๋ค์ด๋ฐ์ผ๋ก ์์ฑํ๊ธฐ.
- Effect๋ฅผ ์ฒ๋ฆฌํ ๋, Core์ Action์ผ๋ก ๋ถ๋ฆฌํ๊ธฐ ๋๋ฌธ์ Effect.Action์ ํตํด ๋ ๋ช ํํ๊ฒ ๊ธฐ๋ฅ ์ญํ ์ ๋ถ๋ฆฌํ ์ ์์
- @Dependency ์ฌ์ฉ
: DependencyKey๋ฅผ ์์๋ฐ์์ ๊ตฌํํด์ผ ํ๋ฉฐ, DependencyValue๋ฅผ ํตํด์ ์์ ํ ์ ์์.
-> ๊ธฐ์กด ํ๋ก์ ํธ์ ์ด์ํ๋ฉด์ ๋ฐ๊ฒฌํ ๋ฌธ์ ์ ๋ฐ ๊ณ ๋ฏผ(DIP๊ฐ ์ด๋์ ํ์ํ๊ฑฐ๊ตฌ๋๋ฅผ ๋๋)
: SDK์์ Network์ ๋ก์ปฌ DB๋ฅผ ๋ค๊ณ ์์.
: @Dependency๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ get-only์ผ๋ก ๋ํ๋จ. (ํ์คํ๊ฒ get-only์ธ์ง๋ ๋ชจ๋ฅด๊ฒ ์ set์ผ๋ก ํ๋ ๋ฐฉ๋ฒ๋ ์์ ๊ฒ ๊ฐ์๋ฐ, ์ด๊ฑด ์กฐ์ฌ๊ธฐ ํ์ํฉ๋๋ค.)
- pullback์ ๋ ๊ฐ๊ฒฐํ๊ฒ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ
: SwiftUI๋ฅผ ์ฌ์ฉํ๋ฉด์ ๋ทฐ์ ๊ณ์ธต์ด ๊น์ด์ง๋ฉด ๋๋๊ณคํ๋๋ฐ, ์ด๋ฒคํธ ์ฒ๋ฆฌ๊ฐ ์์ง ๋ฏธ์ํด์ ํ๋์ ํ์ผ์ ๋๋ ค๋ฐ์ ๋๋์ด ์์ง์์ ์๋ค. ์ด ๋ถ๋ถ์ ์ด๋ป๊ฒ ๊ฐ์ ํ ์ง ๊ผญ ๊ณ ๋ฏผํด๋ณด๊ธฐ
@Depedency(\.sdkService) var sdkService
// โ
๋คํธ์ํฌ ๊ด๋ จ ๋น๋๊ธฐ ์์
์์๋ ๋ฌธ์ ๊ฐ ์์ด ์ฌ์ฉ๊ฐ๋ฅ
sdkService.fetchAllItem
.catchToEffect(Action.fetchAllItemSuccess)
// ํ๋ต
// ๐จ ๋ก์ปฌ DB์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํด์ผ ํ๋ค๋ฉด?
sdkService.location = location // get-only error ๋ฐ์
### ๋ค์ ๋ชฉํ
- TCACoordinator๋ฅผ ์ ์ฉํ์ฌ ํ๋ฉด 3๊ฐ์ด์์ ํ๋ฉด์ ํ์ ๊ตฌํํด๋ณด๊ธฐ.
#### ์์ํ๊ธฐ ์ ์ ์์ฌํญ
https://github.com/pointfreeco/swift-composable-architecture/discussions/1477
Road to 1.0 · Discussion #1477 · pointfreeco/swift-composable-architecture
It’s been almost 2 and a half years since we first released the Composable Architecture, so we’ve been getting more and more questions about its 0.x.x prerelease versioning, what’s holding up a 1.0...
github.com
# Removing the old Reducer struct
โ ReducerProtocol(Reducer) ์ฌ์ฉํด๋ผ.
- 1.0 ๋ฆด๋ฆฌ์ฆ ๋ค๊ฐ์ค๋ฉด AnyReducer fully deprecated(์์ ์ค๋จ) ํ ๊ฑฐ๋ผ๊ณ ํจ.
- ํ์ฌ๋ Deprecated ๋์๋ค๋ ๋ ธ๋์ ๋ฌธ๊ตฌ๊ฐ ๋ํ๋จ.
โ ๋ณ๊ฒฝ ๋ด์ฉ
- typealias Reducer = AnyReducer๋ ์ญ์ ์์ (0.43.0) ๋ฒ์ ์์ ์ ์ญ์ผ๋ก Reducer๋ฅผ ์ฌ์ฉํ๊ณ ์์.
- ReducerProtocol์ ์ด๋ฆ์ Reducer๋ก ๋ณ๊ฒฝ
- Introducing a hard-deprecated typealias ReducerProtocol = Reducer
: ์ธ๋ฒ์งธ๋ ์ ํํ๊ฒ ์๋ฏธ๋ ๋ชจ๋ฅด๊ฒ ๋๋ฐ, ์๋ ์์ด๋ฅผ ์ฐธ๊ณ ํ์๋ฉด Reducer๋ก ํ๋กํ ์ฝ ์ด๋ฆ ์ ์ฉํ๋ ์๋ฏธ์ธ๋ฏ
If you have already updated your code base to 0.43.0, then none of these changes are breaking. You will just have to rename occurrences of ReducerProtocol to Reducer, which Xcode should be able to help with.
# Removing many Combine-specific features of Effect
Concurrency ๋ฆด๋ฆฌ์ฆํ๋ฉด์ (.run, .task, .fireAndForgot)์ ์ฌ์ฉํ๊ณ 3๊ฐ์ง ์ง์ ์ ์ ๋ง์ถ์ด์ ์ ํ์ ๋จ์ํ.
๋ฐ๋ผ์ ๋ถํ์ํ ๋ ๊ฑฐ์๋ฅผ ์ ๊ฑฐํ๊ณ ์ ํจ.
Effect์ ๊ฒฝ์ฐ์๋ Combine publisher๋ก ์ฌ์ฉํ๋๋ก ์ค๊ณ๋์๊ธฐ ๋๋ฌธ์, ๋์ด์ ํ์ํ์ง ์์ ๋ช๋ช ๋ถ๋ถ์์ ์ ๊ฑฐํ๊ธฐ๋ก ํจ.
์๋ฅผ ๋ค์ฌ์ Publisher Protocol์์ Failure๋ก ํ์ง๋ง reducer์์์ Never๋ก ์ ๊ฒจ์๋ ๋ฑ.
๊ทธ๋ฌ๋๊น Failure๋ ๋์ด์ ์๋ฏ๊ฐ ์์ผ๋ฏ๋ก, Effect<Action, Failure> -> Effect<Action>์ผ๋ก ๋จ์ํํ๊ณ ์ ํจ.
- Effect ์ ๊ฑฐํ ๊ฑฐ์ผ.
- EffectTask -> Effect๋ก ์ด๋ฆ ๋ฐ๊ฟ๊ฑฐ์ผ
- ๊ธฐ์กด EffectTask๋ ๊ณ์ ์๋ํ๋๋ก ์ง์ํ ๊ฑฐ์ง๋ง Xcode์์ Deprecated๋๊ฑฐ ์๋ ค์ฃผ์ด์ ๋๊ฐ ๋ณ๊ฒฝํ๋๋ก ๋์ธ๊ฑฐ์ผ.
#### UI ๊ฒฐ๊ณผ๋ฌผ
#### ์ฝ๋
// LocationSetting.swift
//
// LocationSetting.swift
// UJeongApp
//
// Created by Hamlit Jason on 2023/01/03.
import SwiftUI
import ComposableArchitecture
// MARK: - LocationSettingView
public struct LocationSettingView: View {
typealias Core = LocationSettingCore
private let store: StoreOf<Core>
@Environment(\.dismiss) private var dismiss
struct ViewState: Equatable {
let allLocationSection = LocationSection.allSection()
public var selectedLocation: String
init(state: Core.State) {
self.selectedLocation = state.selectedLocation
}
}
enum ViewAction {
case itemSelected(location: String)
case onAppear
}
init(store: StoreOf<Core>) {
self.store = store
}
let columns = [GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())]
public var body: some View {
WithViewStore(
self.store,
observe: ViewState.init,
send: Core.Action.init
) { viewStore in
NavigationView {
ForEach(viewStore.state.allLocationSection) { section in
List {
Text("๋ด๊ฐ ์ ํํ ์ง์ญ: \(viewStore.selectedLocation)")
Section {
LazyVGrid(
columns: columns,
alignment: .center,
spacing: .none
) {
ForEach(section.location.districts, id: \.self) { location in
Text(location)
.padding(.horizontal, 5)
.padding(.vertical, 5)
.overlay {
if location == viewStore.state.selectedLocation {
RoundedRectangle(cornerRadius: 10)
.stroke(Color.black, lineWidth: 3)
}
}
.onTapGesture {
viewStore.send(.itemSelected(location: location))
}
}
}
} header: {
Text(section.location.city)
}
}
.listStyle(.sidebar)
}
.onAppear {
viewStore.send(.onAppear)
}
.navigationBarTitle("์ง์ญ ์ ํ", displayMode: .inline)
.toolbar {
Button {
dismiss()
} label: {
Image(systemName: "checkmark")
}
}
}
}
}
}
// LocationSettingCore.swift
//
// LocationSettingCore.swift
// UJeongApp
//
// Created by Hamlit Jason on 2023/01/16.
//
import Foundation
import ComposableArchitecture
public struct LocationSettingCore: ReducerProtocol {
public struct State: Equatable {
var selectedLocation: String = ""
}
public enum Action {
case onAppear
case itemSelected(location: String)
case updateAppStorage
init(action: LocationSettingView.ViewAction) {
switch action {
case .onAppear:
self = .onAppear
case let .itemSelected(location):
self = .itemSelected(location: location)
}
}
}
// NOTE: - func reduce๋ ๋์ผ
public var body: some ReducerProtocol<State, Action> {
Reduce { state, action in
switch action {
case .onAppear:
state.selectedLocation = sdkService.selectedLocation
return .none
case let .itemSelected(location):
state.selectedLocation = location
return EffectTask<Action>(value: .updateAppStorage)
case .updateAppStorage:
sdkService.selectedLocation = state.selectedLocation
return .none
}
}
}
private let sdkService = UJeongSDKService()
}
// LocationSection.Swift
//
// LocationSection.swift
// UJeongApp
//
// Created by Hamlit Jason on 2023/01/16.
//
import Foundation
struct LocationSection: Identifiable, Equatable {
let id = UUID()
let location: Location
static func allSection() -> [LocationSection] {
let seoul = LocationSection(location: .init(city: "์์ธํน๋ณ์",
districts: Gu.allCases.map { $0.rawValue }.sorted(by: <)))
return [seoul]
}
}
// Gu.Swift
//
// Location.swift
// UJeongApp
//
// Created by Hamlit Jason on 2023/01/04.
//
import Foundation
/*
๋๋์๋ ๊ตฌ๋ก ๋ถ๋ฅํ ์ ์๋ค๋ฉด ๊ตฌ๋จ์๋ก ๋ถ๋ฅํฉ๋๋ค.
๋ง์ฝ ์ง๋ฐฉ ์๋์์ ๊ฒฝ์ฐ์๋ ๊ตฌ๋จ์๋ก ๋ถ๋ฅํ๊ธฐ๊ฐ ์ด๋ ต๋ค๋ฉด ์๋จ์๋ก ๋ถ๋ฅํ ์ ์์ต๋๋ค.
์ฐจํ ํ์ฅ์ฑ์ ๋ ์๊ฐํฉ์๋ค.
*/
public struct Location: Hashable {
public var city: String // ๋์์ด๋ฆ
public var districts: [String] // ์ง์ญ๊ตฌ
}
public enum Gu: String, CaseIterable {
case ๊ฐ์๊ตฌ
case ์์ฒ๊ตฌ
case ๊ตฌ๋ก๊ตฌ
case ๊ธ์ฒ๊ตฌ
case ๊ด์
๊ตฌ
case ์๋ฑํฌ๊ตฌ
case ๋์๊ตฌ
case ์์ด๊ตฌ
case ๊ฐ๋จ๊ตฌ
case ์กํ๊ตฌ
case ๊ฐ๋๊ตฌ
case ์ฉ์ฐ๊ตฌ
case ์ฑ๋๊ตฌ
case ๊ด์ง๊ตฌ
case ๋๋๋ฌธ๊ตฌ
case ์ค๋๊ตฌ
case ๋
ธ์๊ตฌ
case ๋๋ด๊ตฌ
case ๊ฐ๋ถ๊ตฌ
case ์ฑ๋ถ๊ตฌ
case ์ข
๋ก๊ตฌ
case ์๋๋ฌธ๊ตฌ
case ์ค๊ตฌ
case ๋งํฌ๊ตฌ
case ์ํ๊ตฌ
}
'apple > ๐ฆฅ TCA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[TCA] SharedState (1) | 2023.09.27 |
---|---|
[TCA] OptionalState (IfLetCase) (0) | 2023.09.27 |
[TCA] FocusState (0) | 2023.09.27 |
[TCA] Binding (0) | 2023.09.27 |
[TCA] Tutorial #5 (Multiple presentation destinations) (0) | 2023.09.24 |