Skip to content

Commit 686f4d3

Browse files
committed
refactor: BindingAction 적용
1 parent 27c9aba commit 686f4d3

3 files changed

Lines changed: 43 additions & 83 deletions

File tree

Application/DevLogPresentation/Sources/Home/Editor/TodoEditorFeature.swift

Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -101,23 +101,13 @@ struct TodoEditorFeature {
101101
case updated(Todo)
102102
}
103103

104-
enum Action: Equatable {
104+
enum Action: BindableAction, Equatable {
105105
case alert(PresentationAction<Never>)
106+
case binding(BindingAction<State>)
106107
case onAppear
107108
case addTag(String)
108109
case removeTag(String)
109-
case setContent(String)
110110
case setCompleted(Bool)
111-
case setDueDate(Date?)
112-
case setCategory(TodoCategoryItem)
113-
case setPinned(Bool)
114-
case setShowInfo(Bool)
115-
case setSelectedTodoId(TodoIdItem?)
116-
case setTabViewTag(Tag)
117-
case setTagText(String)
118-
case setTitle(String)
119-
case setCategories([TodoCategoryItem])
120-
case setReferenceItems([Int: TodoReferenceItem])
121111
case upsertTodo
122112
case createSucceeded
123113
case saveFailed
@@ -135,56 +125,41 @@ struct TodoEditorFeature {
135125
Scope(state: \.loading, action: \.loading) {
136126
LoadingFeature()
137127
}
128+
BindingReducer()
138129
Reduce { state, action in
139130
switch action {
140131
case .alert:
141132
break
142-
case .onAppear:
143-
return fetchCategoriesEffect()
144-
case .addTag(let tag):
145-
if !tag.isEmpty {
146-
state.tags.append(tag)
147-
}
148-
case .removeTag(let tagText):
149-
state.tags.removeAll { $0 == tagText }
150-
case .setContent(let content):
151-
state.content = content
133+
case .binding(\.content):
152134
if state.tabViewTag == .preview {
153135
return resolveMarkdownEffect(content: state.content)
154136
}
155-
case .setTagText(let tagText):
156-
state.tagText = tagText
157-
case .setTitle(let title):
158-
state.title = title
159-
case .setDueDate(let dueDate):
137+
case .binding(\.dueDate):
160138
if let tomorrowDate = Calendar.current.date(byAdding: .day, value: 1, to: now),
161-
let dueDate {
139+
let dueDate = state.dueDate {
162140
state.dueDate = max(dueDate, tomorrowDate)
163141
} else {
164142
state.dueDate = nil
165143
}
144+
case .binding(\.tabViewTag):
145+
if state.tabViewTag == .preview {
146+
return resolveMarkdownEffect(content: state.content)
147+
}
148+
case .binding:
149+
break
150+
case .onAppear:
151+
return fetchCategoriesEffect()
152+
case .addTag(let tag):
153+
if !tag.isEmpty {
154+
state.tags.append(tag)
155+
}
156+
case .removeTag(let tagText):
157+
state.tags.removeAll { $0 == tagText }
166158
case .setCompleted(let isCompleted):
167159
if state.isCompleted != isCompleted {
168160
state.completedAt = isCompleted ? now : nil
169161
}
170162
state.isCompleted = isCompleted
171-
case .setCategory(let item):
172-
state.category = item
173-
case .setPinned(let isPinned):
174-
state.isPinned = isPinned
175-
case .setShowInfo(let isPresented):
176-
state.showInfo = isPresented
177-
case .setSelectedTodoId(let todoId):
178-
state.selectedTodoId = todoId
179-
case .setTabViewTag(let tag):
180-
state.tabViewTag = tag
181-
if tag == .preview {
182-
return resolveMarkdownEffect(content: state.content)
183-
}
184-
case .setCategories(let categories):
185-
state.categories = categories
186-
case .setReferenceItems(let items):
187-
state.referenceItems = items
188163
case .upsertTodo:
189164
state.saveResult = nil
190165
if state.originalDraft == nil {
@@ -254,7 +229,7 @@ private extension TodoEditorFeature {
254229
.run { [fetchPreferencesUseCase] send in
255230
do {
256231
let preferences = try await fetchPreferencesUseCase.execute()
257-
await send(.setCategories(preferences.map(TodoCategoryItem.init(from:))))
232+
await send(.binding(.set(\.categories, preferences.map(TodoCategoryItem.init(from:)))))
258233
} catch { }
259234
}
260235
}
@@ -273,7 +248,7 @@ private extension TodoEditorFeature {
273248
}
274249
}
275250

276-
await send(.setReferenceItems(referenceItems))
251+
await send(.binding(.set(\.referenceItems, referenceItems)))
277252
}
278253
}
279254

Application/DevLogPresentation/Sources/Home/Editor/TodoEditorView.swift

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,12 @@ struct TodoEditorView: View {
5353
.navigationTitle(store.navigationTitle)
5454
.navigationBarTitleDisplayMode(.inline)
5555
.toolbarBackground(.background, for: .navigationBar)
56-
.sheet(isPresented: Binding(
57-
get: { store.showInfo },
58-
set: { store.send(.setShowInfo($0)) }
59-
)) {
56+
.sheet(isPresented: $store.showInfo) {
6057
TodoEditorInfoSheetView(store: store) {
61-
store.send(.setShowInfo(false))
58+
store.send(.binding(.set(\.showInfo, false)))
6259
}
6360
}
64-
.sheet(item: Binding(
65-
get: { store.selectedTodoId },
66-
set: { store.send(.setSelectedTodoId($0)) }
67-
)) { item in
61+
.sheet(item: $store.selectedTodoId) { item in
6862
NavigationStack {
6963
TodoDetailView(store: Store(
7064
initialState: TodoDetailFeature.State(todoId: item.id, showEditButton: false)
@@ -76,7 +70,7 @@ struct TodoEditorView: View {
7670
})
7771
.toolbar {
7872
ToolbarLeadingButton {
79-
store.send(.setSelectedTodoId(nil))
73+
store.send(.binding(.set(\.selectedTodoId, nil)))
8074
}
8175
}
8276
}
@@ -89,7 +83,7 @@ struct TodoEditorView: View {
8983
}
9084
ToolbarItem(placement: .topBarTrailing) {
9185
Button {
92-
store.send(.setShowInfo(true))
86+
store.send(.binding(.set(\.showInfo, true)))
9387
} label: {
9488
Image(systemName: "info.circle")
9589
}
@@ -122,10 +116,7 @@ struct TodoEditorView: View {
122116
private var titleField: some View {
123117
TextField(
124118
"",
125-
text: Binding(
126-
get: { store.title },
127-
set: { store.send(.setTitle($0)) }
128-
),
119+
text: $store.title,
129120
prompt: Text(String(localized: "todo_editor_title_required")).foregroundColor(Color.secondary),
130121
)
131122
.font(.title2)
@@ -140,7 +131,7 @@ struct TodoEditorView: View {
140131
get: { store.tabViewTag },
141132
set: { tag in
142133
if tag == .editor {
143-
store.send(.setTabViewTag(.editor))
134+
store.send(.binding(.set(\.tabViewTag, .editor)))
144135
field = .content
145136
} else {
146137
transitionToPreview()
@@ -162,10 +153,7 @@ struct TodoEditorView: View {
162153
VStack(alignment: .leading, spacing: 8) {
163154
markdownHint
164155
UIKitTextEditor(
165-
text: Binding(
166-
get: { store.content },
167-
set: { store.send(.setContent($0)) }
168-
),
156+
text: $store.content,
169157
placeholder: String(localized: "todo_editor_description_optional")
170158
)
171159
.focused($field, equals: .content)
@@ -177,7 +165,7 @@ struct TodoEditorView: View {
177165
TodoMarkdownContentView(
178166
content: store.content,
179167
referenceItems: store.referenceItems,
180-
onOpenTodoID: { store.send(.setSelectedTodoId(TodoIdItem(id: $0))) }
168+
onOpenTodoID: { store.send(.binding(.set(\.selectedTodoId, TodoIdItem(id: $0)))) }
181169
)
182170
}
183171
}
@@ -220,7 +208,7 @@ struct TodoEditorView: View {
220208
field = nil
221209

222210
DispatchQueue.main.async {
223-
store.send(.setTabViewTag(.preview))
211+
store.send(.binding(.set(\.tabViewTag, .preview)))
224212
}
225213
}
226214

@@ -261,7 +249,7 @@ private struct TodoEditorInfoSheetView: View {
261249
return
262250
}
263251

264-
store.send(.setCategory(item))
252+
store.send(.binding(.set(\.category, item)))
265253
}
266254
)
267255
) {
@@ -284,7 +272,7 @@ private struct TodoEditorInfoSheetView: View {
284272
String(localized: "todo_pinned"),
285273
isOn: Binding(
286274
get: { store.isPinned },
287-
set: { store.send(.setPinned($0)) }
275+
set: { store.send(.binding(.set(\.isPinned, $0))) }
288276
)
289277
)
290278
.tint(.blue)
@@ -296,10 +284,7 @@ private struct TodoEditorInfoSheetView: View {
296284
HStack(spacing: 12) {
297285
TextField(
298286
String(localized: "todo_add"),
299-
text: Binding(
300-
get: { store.tagText },
301-
set: { store.send(.setTagText($0)) }
302-
)
287+
text: $store.tagText
303288
)
304289
.frame(height: UIFont.preferredFont(forTextStyle: .title2).lineHeight)
305290
.textInputAutocapitalization(.never)
@@ -346,15 +331,15 @@ private struct TodoEditorInfoSheetView: View {
346331
private var dueDateControl: some View {
347332
DueDatePicker(selection: Binding(
348333
get: { store.dueDate ?? Date() },
349-
set: { store.send(.setDueDate($0)) }
334+
set: { store.send(.binding(.set(\.dueDate, $0))) }
350335
)) {
351336
HStack {
352337
Text(String(localized: "todo_due_date"))
353338
.foregroundStyle(.primary)
354339
Spacer()
355340
if let dueDate = store.dueDate {
356341
Tag(dueDateText(for: dueDate), isEditing: true) {
357-
store.send(.setDueDate(nil))
342+
store.send(.binding(.set(\.dueDate, nil)))
358343
}
359344
.padding(.vertical, -4)
360345
} else {
@@ -370,7 +355,7 @@ private struct TodoEditorInfoSheetView: View {
370355

371356
let tagText = normalizedTagText
372357
store.send(.addTag(tagText))
373-
store.send(.setTagText(""))
358+
store.send(.binding(.set(\.tagText, "")))
374359
}
375360

376361
private var normalizedTagText: String {

Application/DevLogPresentation/Tests/Home/TodoEditorFeatureTestDoubles.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ final class TodoEditorStoreTestAdapter {
9696
}
9797

9898
func setContent(_ content: String) async {
99-
await store.send(.setContent(content)) {
99+
await store.send(.binding(.set(\.content, content))) {
100100
$0.content = content
101101
}
102102
await drainReceivedActions()
@@ -114,26 +114,26 @@ final class TodoEditorStoreTestAdapter {
114114

115115
func setDueDate(_ dueDate: Date?) async {
116116
let expectedDueDate = expectedDueDate(for: dueDate)
117-
await store.send(.setDueDate(dueDate)) {
117+
await store.send(.binding(.set(\.dueDate, dueDate))) {
118118
$0.dueDate = expectedDueDate
119119
}
120120
}
121121

122122
func setPinned(_ isPinned: Bool) async {
123-
await store.send(.setPinned(isPinned)) {
123+
await store.send(.binding(.set(\.isPinned, isPinned))) {
124124
$0.isPinned = isPinned
125125
}
126126
}
127127

128128
func setTab(_ tab: TodoEditorFeature.Tag) async {
129-
await store.send(.setTabViewTag(tab)) {
129+
await store.send(.binding(.set(\.tabViewTag, tab))) {
130130
$0.tabViewTag = tab
131131
}
132132
await drainReceivedActions()
133133
}
134134

135135
func setTitle(_ title: String) async {
136-
await store.send(.setTitle(title)) {
136+
await store.send(.binding(.set(\.title, title))) {
137137
$0.title = title
138138
}
139139
await drainReceivedActions()

0 commit comments

Comments
 (0)