Skip to content

Commit f4f54dd

Browse files
committed
refactor: view / store 간 액션 분리
1 parent 64f00d4 commit f4f54dd

5 files changed

Lines changed: 163 additions & 133 deletions

File tree

Application/DevLogPresentation/Sources/Home/Home/HomeFeature+Effects.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extension HomeFeature {
2121
.publisher { [networkConnectivityUseCase] in
2222
networkConnectivityUseCase.observe()
2323
.receive(on: DispatchQueue.main)
24-
.map(Action.networkStatusChanged)
24+
.map { .store(.networkStatusChanged($0)) }
2525
}
2626
.cancellable(id: CancelID.networkConnectivity, cancelInFlight: true)
2727
}
@@ -33,7 +33,7 @@ extension HomeFeature {
3333
let preferences = try await fetchPreferencesUseCase.execute()
3434
await send(.store(.setTodoCategory(preferences.map(TodoCategoryItem.init(from:)))))
3535
} catch {
36-
await send(.setAlert(isPresented: true, type: .error))
36+
await send(.store(.setAlert(isPresented: true, type: .error)))
3737
}
3838
await send(.loading(.end(target: LoadingTarget.preferences.target, mode: .immediate)))
3939
}
@@ -50,7 +50,7 @@ extension HomeFeature {
5050
.compactMap(RecentTodoItem.init(from:))
5151
await send(.store(.updateRecentTodos(Array(items))))
5252
} catch {
53-
await send(.setAlert(isPresented: true, type: .error))
53+
await send(.store(.setAlert(isPresented: true, type: .error)))
5454
}
5555
await send(.loading(.end(target: LoadingTarget.recentTodos.target, mode: .immediate)))
5656
}
@@ -63,7 +63,7 @@ extension HomeFeature {
6363
let pages = try await fetchWebPagesUseCase.execute("")
6464
await send(.store(.updateWebPages(pages.map(WebPageItem.init(from:)))))
6565
} catch {
66-
await send(.setAlert(isPresented: true, type: .error))
66+
await send(.store(.setAlert(isPresented: true, type: .error)))
6767
}
6868
await send(.loading(.end(target: LoadingTarget.webPage.target, mode: .immediate)))
6969
}
@@ -78,7 +78,7 @@ extension HomeFeature {
7878
let pages = try await fetchWebPagesUseCase.execute("")
7979
await send(.store(.updateWebPages(pages.map(WebPageItem.init(from:)))))
8080
} catch {
81-
await send(.setAlert(isPresented: true, type: .error))
81+
await send(.store(.setAlert(isPresented: true, type: .error)))
8282
}
8383
await send(.loading(.end(target: LoadingTarget.overlay.target, mode: .delayed)))
8484
}
@@ -89,8 +89,8 @@ extension HomeFeature {
8989
do {
9090
try await deleteWebPageUseCase.execute(page.url.absoluteString)
9191
} catch {
92-
await send(.handleWebPageDeleteFailure(page.id))
93-
await send(.setAlert(isPresented: true, type: .error))
92+
await send(.store(.handleWebPageDeleteFailure(page.id)))
93+
await send(.store(.setAlert(isPresented: true, type: .error)))
9494
}
9595
}
9696
}
@@ -102,9 +102,9 @@ extension HomeFeature {
102102
try await addWebPageUseCase.execute(urlString)
103103
} catch {
104104
if let webPageURL = URL(string: urlString) {
105-
await send(.setWebPageHidden(webPageURL, true))
105+
await send(.store(.setWebPageHidden(webPageURL, true)))
106106
}
107-
await send(.setAlert(isPresented: true, type: .error))
107+
await send(.store(.setAlert(isPresented: true, type: .error)))
108108
}
109109
}
110110
}
@@ -114,7 +114,7 @@ extension HomeFeature {
114114
do {
115115
try await updatePreferencesUseCase.execute(items.map(\.preference))
116116
} catch {
117-
await send(.setAlert(isPresented: true, type: .error))
117+
await send(.store(.setAlert(isPresented: true, type: .error)))
118118
}
119119
}
120120
}
@@ -123,7 +123,7 @@ extension HomeFeature {
123123
.run { [clock] send in
124124
// iOS 17에서 시트 dismiss 직후 fullScreenCover를 바로 올리지 않도록 하기 위해서 0.1초 딜레이
125125
try await clock.sleep(for: .seconds(0.1))
126-
await send(.setPresentation(.todoEditor, true))
126+
await send(.store(.setPresentation(.todoEditor, true)))
127127
}
128128
.cancellable(id: CancelID.delayedTodoEditor, cancelInFlight: true)
129129
}

Application/DevLogPresentation/Sources/Home/Home/HomeFeature.swift

Lines changed: 123 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,31 @@ struct HomeFeature {
5050
case alert(PresentationAction<Never>)
5151
case sheet(PresentationAction<Sheet>)
5252
case fullScreenCover(PresentationAction<Never>)
53-
case startObserving
54-
case fetchData
55-
case refreshRecentTodos
56-
case networkStatusChanged(Bool)
57-
case setSheet(SheetState?)
58-
case setPresentation(Presentation, Bool)
59-
case setAlert(isPresented: Bool, type: AlertType? = nil)
60-
case refreshWebPages
61-
case setWebPageHidden(URL, Bool)
62-
case handleWebPageDeleteFailure(URL)
63-
case finishDeleteWebPageToast(String)
64-
case tapTodoCategory(TodoCategory)
65-
case orderTodoCategory([TodoCategoryItem])
66-
case updateWebPageURLInput(String)
67-
case addWebPage
68-
case deleteWebPage(WebPageItem)
69-
case undoDeleteWebPage
53+
case view(ViewAction)
7054
case store(StoreAction)
7155
case loading(LoadingFeature.Action)
7256

57+
enum ViewAction: Equatable {
58+
case startObserving
59+
case fetchData
60+
case refreshRecentTodos
61+
case refreshWebPages
62+
case finishDeleteWebPageToast(String)
63+
case tapTodoCategory(TodoCategory)
64+
case orderTodoCategory([TodoCategoryItem])
65+
case updateWebPageURLInput(String)
66+
case addWebPage
67+
case deleteWebPage(WebPageItem)
68+
case undoDeleteWebPage
69+
}
70+
7371
enum StoreAction: Equatable {
72+
case networkStatusChanged(Bool)
73+
case setSheet(SheetState?)
74+
case setPresentation(Presentation, Bool)
75+
case setAlert(isPresented: Bool, type: AlertType? = nil)
76+
case setWebPageHidden(URL, Bool)
77+
case handleWebPageDeleteFailure(URL)
7478
case setTodoCategory([TodoCategoryItem])
7579
case updateRecentTodos([RecentTodoItem])
7680
case updateWebPages([WebPageItem])
@@ -193,82 +197,10 @@ struct HomeFeature {
193197
state.sheet = nil
194198
case .sheet:
195199
break
196-
case .startObserving:
197-
return observeNetworkConnectivityEffect()
198-
case .fetchData:
199-
return .merge(
200-
fetchTodoCategoryPreferencesEffect(),
201-
fetchRecentTodosEffect(),
202-
fetchWebPagesEffect()
203-
)
204-
case .refreshRecentTodos:
205-
return fetchRecentTodosEffect()
206-
case .networkStatusChanged(let isConnected):
207-
state.isNetworkConnected = isConnected
208-
case .setSheet(let sheet):
209-
state.sheet = sheet
210-
case .setPresentation(let presentation, let isPresented):
211-
Self.setPresentation(&state, presentation: presentation, isPresented: isPresented)
212-
case .setAlert(let isPresented, let type):
213-
Self.setAlert(&state, isPresented: isPresented, type: type)
214-
case .refreshWebPages:
215-
return fetchWebPagesEffect()
216-
case .setWebPageHidden(let webPageURL, let isHidden):
217-
if let index = state.webPages.firstIndex(where: { $0.id == webPageURL }) {
218-
state.webPages[index].isHidden = isHidden
219-
}
220-
case .handleWebPageDeleteFailure(let webPageURL):
221-
if let index = state.webPages.firstIndex(where: { $0.id == webPageURL }) {
222-
state.webPages[index].isHidden = false
223-
} else {
224-
state.needsWebPageRefresh = true
225-
}
226-
case .finishDeleteWebPageToast(let urlString):
227-
state.webPages.removeAll { $0.url.absoluteString == urlString && $0.isHidden }
228-
if state.deletedWebPageURLString == urlString {
229-
state.deletedWebPageURLString = nil
230-
}
231-
case .tapTodoCategory(let category):
232-
state.selectedTodoCategory = category
233-
state.sheet = nil
234-
return delayedTodoEditorEffect()
235-
case .orderTodoCategory(let preferences):
236-
state.preferences = preferences
237-
state.recentTodos = Self.syncRecentTodos(state.recentTodos, preferences: preferences)
238-
state.sheet = nil
239-
return updateTodoCategoryPreferencesEffect(preferences)
240-
case .updateWebPageURLInput(let text):
241-
state.webPageURLInput = text
242-
case .addWebPage:
243-
guard let normalizedURL = Self.normalizedWebPageURL(state.webPageURLInput) else {
244-
Self.setAlert(&state, isPresented: true, type: .invalidURL)
245-
return .none
246-
}
247-
state.sheet = nil
248-
Self.setAlert(&state, isPresented: false, type: nil)
249-
return addWebPageEffect(normalizedURL)
250-
case .deleteWebPage(let page):
251-
guard let index = state.webPages.firstIndex(where: { $0.id == page.id }) else {
252-
return .none
253-
}
254-
state.deletedWebPageURLString = page.url.absoluteString
255-
state.webPages[index].isHidden = true
256-
return deleteWebPageEffect(page)
257-
case .undoDeleteWebPage:
258-
guard let urlString = state.deletedWebPageURLString else { return .none }
259-
if let index = state.webPages.firstIndex(where: { $0.url.absoluteString == urlString }) {
260-
state.webPages[index].isHidden = false
261-
}
262-
state.deletedWebPageURLString = nil
263-
return undoDeleteWebPageEffect(urlString)
264-
case .store(.setTodoCategory(let preferences)):
265-
state.preferences = preferences
266-
state.recentTodos = Self.syncRecentTodos(state.recentTodos, preferences: preferences)
267-
case .store(.updateRecentTodos(let todos)):
268-
state.recentTodos = todos
269-
case .store(.updateWebPages(let pages)):
270-
state.webPages = pages
271-
state.needsWebPageRefresh = false
200+
case .view(let action):
201+
return reduce(action, state: &state)
202+
case .store(let action):
203+
return reduce(action, state: &state)
272204
case .loading:
273205
break
274206
}
@@ -282,6 +214,104 @@ struct HomeFeature {
282214
}
283215
}
284216

217+
private extension HomeFeature {
218+
func reduce(
219+
_ action: Action.ViewAction,
220+
state: inout State
221+
) -> Effect<Action> {
222+
switch action {
223+
case .startObserving:
224+
return observeNetworkConnectivityEffect()
225+
case .fetchData:
226+
return .merge(
227+
fetchTodoCategoryPreferencesEffect(),
228+
fetchRecentTodosEffect(),
229+
fetchWebPagesEffect()
230+
)
231+
case .refreshRecentTodos:
232+
return fetchRecentTodosEffect()
233+
case .refreshWebPages:
234+
return fetchWebPagesEffect()
235+
case .finishDeleteWebPageToast(let urlString):
236+
state.webPages.removeAll { $0.url.absoluteString == urlString && $0.isHidden }
237+
if state.deletedWebPageURLString == urlString {
238+
state.deletedWebPageURLString = nil
239+
}
240+
case .tapTodoCategory(let category):
241+
state.selectedTodoCategory = category
242+
state.sheet = nil
243+
return delayedTodoEditorEffect()
244+
case .orderTodoCategory(let preferences):
245+
state.preferences = preferences
246+
state.recentTodos = Self.syncRecentTodos(state.recentTodos, preferences: preferences)
247+
state.sheet = nil
248+
return updateTodoCategoryPreferencesEffect(preferences)
249+
case .updateWebPageURLInput(let text):
250+
state.webPageURLInput = text
251+
case .addWebPage:
252+
guard let normalizedURL = Self.normalizedWebPageURL(state.webPageURLInput) else {
253+
Self.setAlert(&state, isPresented: true, type: .invalidURL)
254+
return .none
255+
}
256+
state.sheet = nil
257+
Self.setAlert(&state, isPresented: false, type: nil)
258+
return addWebPageEffect(normalizedURL)
259+
case .deleteWebPage(let page):
260+
guard let index = state.webPages.firstIndex(where: { $0.id == page.id }) else {
261+
return .none
262+
}
263+
state.deletedWebPageURLString = page.url.absoluteString
264+
state.webPages[index].isHidden = true
265+
return deleteWebPageEffect(page)
266+
case .undoDeleteWebPage:
267+
guard let urlString = state.deletedWebPageURLString else { return .none }
268+
if let index = state.webPages.firstIndex(where: { $0.url.absoluteString == urlString }) {
269+
state.webPages[index].isHidden = false
270+
}
271+
state.deletedWebPageURLString = nil
272+
return undoDeleteWebPageEffect(urlString)
273+
}
274+
275+
return .none
276+
}
277+
278+
func reduce(
279+
_ action: Action.StoreAction,
280+
state: inout State
281+
) -> Effect<Action> {
282+
switch action {
283+
case .networkStatusChanged(let isConnected):
284+
state.isNetworkConnected = isConnected
285+
case .setSheet(let sheet):
286+
state.sheet = sheet
287+
case .setPresentation(let presentation, let isPresented):
288+
Self.setPresentation(&state, presentation: presentation, isPresented: isPresented)
289+
case .setAlert(let isPresented, let type):
290+
Self.setAlert(&state, isPresented: isPresented, type: type)
291+
case .setWebPageHidden(let webPageURL, let isHidden):
292+
if let index = state.webPages.firstIndex(where: { $0.id == webPageURL }) {
293+
state.webPages[index].isHidden = isHidden
294+
}
295+
case .handleWebPageDeleteFailure(let webPageURL):
296+
if let index = state.webPages.firstIndex(where: { $0.id == webPageURL }) {
297+
state.webPages[index].isHidden = false
298+
} else {
299+
state.needsWebPageRefresh = true
300+
}
301+
case .setTodoCategory(let preferences):
302+
state.preferences = preferences
303+
state.recentTodos = Self.syncRecentTodos(state.recentTodos, preferences: preferences)
304+
case .updateRecentTodos(let todos):
305+
state.recentTodos = todos
306+
case .updateWebPages(let pages):
307+
state.webPages = pages
308+
state.needsWebPageRefresh = false
309+
}
310+
311+
return .none
312+
}
313+
}
314+
285315
private struct HomeSheetFeature: Reducer {
286316
typealias State = HomeFeature.SheetState
287317
typealias Action = HomeFeature.Sheet

0 commit comments

Comments
 (0)