@@ -14,10 +14,11 @@ final class TodoViewModel: Store {
1414 let kind : TodoKind
1515 var showEditor : Bool = false
1616 var showAlert : Bool = false
17+ var alertTitle : String = " "
1718 var alertMessage : String = " "
1819 var scope : TodoScope = . title
1920 var filterOption : FilterOption = . create
20- var isLoading = false
21+ var isLoading : Bool = false
2122 var showToast : Bool = false
2223 var toastMessage : String = " "
2324 var pendingTask : ( Todo , Int ) ?
@@ -29,31 +30,26 @@ final class TodoViewModel: Store {
2930
3031 enum Action {
3132 // User
32- case tapTogglePinned( Todo )
33+ case refresh
34+ case setAlert( Bool )
35+ case setShowEditor( Bool )
3336 case swipeTodo( Todo )
3437 case tapFilterOption( FilterOption )
35- case upsertTodo ( Todo )
38+ case tapTogglePinned ( Todo )
3639 case undoDelete
37- case confirmDelete
3840
3941 // View
40- case onAppear, refresh
41- case openEditor
42- case closeEditor
43- case closeAlert
42+ case confirmDelete
43+ case onAppear
4444 case setScope( TodoScope )
4545 case setSearchText( String )
46- case setToast( isPresented: Bool , type: ToastType ? = nil )
47- case setLoading( Bool )
48- case setTodos( [ Todo ] )
46+ case setToast( isPresented: Bool )
47+ case upsertTodo( Todo )
4948
5049 // Run
51- case didShowAlert( String )
5250 case didTogglePinned( Todo )
53- }
54-
55- enum ToastType {
56- case delete
51+ case setLoading( Bool )
52+ case setTodos( [ Todo ] )
5753 }
5854
5955 enum SideEffect {
@@ -82,59 +78,21 @@ final class TodoViewModel: Store {
8278
8379 func reduce( with action: Action ) -> [ SideEffect ] {
8480 var state = self . state
85-
81+ var effects : [ SideEffect ] = [ ]
82+
8683 switch action {
87- case . onAppear, . refresh:
88- return [ . fetch]
89- case . tapTogglePinned( let todo) :
90- return [ . togglePinned( todo) ]
91- case . swipeTodo( let todo) :
92- guard let index = state. todos. firstIndex ( where: { $0. id == todo. id } ) else {
93- return [ ]
94- }
95- state. pendingTask = ( todo, index)
96- state. todos. remove ( at: index)
97- setToast ( & state, isPresented: true , for: . delete)
98- case . tapFilterOption( let option) :
99- state. filterOption = option
100- case . upsertTodo( let todo) :
101- return [ . upsert( todo) ]
102- case . undoDelete:
103- guard let ( todo, index) = state. pendingTask else { return [ ] }
104- state. todos. insert ( todo, at: index)
105- state. pendingTask = nil
106- case . confirmDelete:
107- guard let ( item, _) = state. pendingTask else {
108- return [ ]
109- }
110- return [ . delete( item) ]
111- case . openEditor:
112- state. showEditor = true
113- case . closeEditor:
114- state. showEditor = false
115- case . closeAlert:
116- state. showAlert = false
117- case . setScope( let scope) :
118- state. scope = scope
119- case . setSearchText( let text) :
120- state. searchText = text
121- case . setToast( let isPresented, let type) :
122- setToast ( & state, isPresented: isPresented, for: type)
123- case . setLoading( let value) :
124- state. isLoading = value
125- case . setTodos( let todos) :
126- state. todos = todos
127- case . didShowAlert( let message) :
128- state. alertMessage = message
129- state. showAlert = true
130- case . didTogglePinned( let todo) :
131- if let index = state. todos. firstIndex ( where: { $0. id == todo. id } ) {
132- state. todos [ index] = todo
133- }
84+ case . refresh, . setAlert, . setShowEditor, . swipeTodo, . tapFilterOption, . tapTogglePinned, . undoDelete:
85+ effects = reduceByUser ( action, state: & state)
86+
87+ case . confirmDelete, . onAppear, . setScope, . setSearchText, . setToast, . upsertTodo:
88+ effects = reduceByView ( action, state: & state)
89+
90+ case . didTogglePinned, . setLoading, . setTodos:
91+ effects = reduceByRun ( action, state: & state)
13492 }
135-
93+
13694 self . state = state
137- return [ ]
95+ return effects
13896 }
13997
14098 func run( _ effect: SideEffect ) {
@@ -147,7 +105,7 @@ final class TodoViewModel: Store {
147105 let todos = try await fetchTodosByKindUseCase. execute ( state. kind)
148106 send ( . setTodos( todos) )
149107 } catch {
150- send ( . didShowAlert ( error . localizedDescription ) )
108+ send ( . setAlert ( true ) )
151109 }
152110 }
153111 case . upsert( let item) :
@@ -158,7 +116,7 @@ final class TodoViewModel: Store {
158116 try await upsertTodoUseCase. execute ( item)
159117 send ( . refresh)
160118 } catch {
161- send ( . didShowAlert ( error . localizedDescription ) )
119+ send ( . setAlert ( true ) )
162120 }
163121 }
164122 case . togglePinned( let item) :
@@ -171,32 +129,108 @@ final class TodoViewModel: Store {
171129 try await upsertTodoUseCase. execute ( todo)
172130 send ( . didTogglePinned( todo) )
173131 } catch {
174- send ( . didShowAlert ( error . localizedDescription ) )
132+ send ( . setAlert ( true ) )
175133 }
176134 }
177135 case . delete( let item) :
178136 Task {
179137 do {
180138 try await deleteTodoUseCase. execute ( item. id)
181139 } catch {
182- send ( . didShowAlert ( error . localizedDescription ) )
140+ send ( . setAlert ( true ) )
183141 }
184142 }
185143 }
186144 }
187145}
146+
147+ // MARK: - Reduce Methods
148+ private extension TodoViewModel {
149+ func reduceByUser( _ action: Action , state: inout State ) -> [ SideEffect ] {
150+ switch action {
151+ case . refresh:
152+ return [ . fetch]
153+ case . setAlert( let value) :
154+ setAlert ( & state, isPresented: value)
155+ case . setShowEditor( let value) :
156+ state. showEditor = value
157+ case . swipeTodo( let todo) :
158+ guard let index = state. todos. firstIndex ( where: { $0. id == todo. id } ) else {
159+ return [ ]
160+ }
161+ state. pendingTask = ( todo, index)
162+ state. todos. remove ( at: index)
163+ setToast ( & state, isPresented: true )
164+ case . tapFilterOption( let option) :
165+ state. filterOption = option
166+ case . tapTogglePinned( let todo) :
167+ return [ . togglePinned( todo) ]
168+ case . undoDelete:
169+ guard let ( todo, index) = state. pendingTask else { return [ ] }
170+ state. todos. insert ( todo, at: index)
171+ state. pendingTask = nil
172+ default :
173+ break
174+ }
175+ return [ ]
176+ }
177+
178+ func reduceByView( _ action: Action , state: inout State ) -> [ SideEffect ] {
179+ switch action {
180+ case . confirmDelete:
181+ guard let ( item, _) = state. pendingTask else {
182+ return [ ]
183+ }
184+ return [ . delete( item) ]
185+ case . onAppear:
186+ return [ . fetch]
187+ case . setScope( let scope) :
188+ state. scope = scope
189+ case . setSearchText( let text) :
190+ state. searchText = text
191+ case . setToast( let isPresented) :
192+ setToast ( & state, isPresented: isPresented)
193+ case . upsertTodo( let todo) :
194+ return [ . upsert( todo) ]
195+ default :
196+ break
197+ }
198+ return [ ]
199+ }
200+
201+ func reduceByRun( _ action: Action , state: inout State ) -> [ SideEffect ] {
202+ switch action {
203+ case . didTogglePinned( let todo) :
204+ if let index = state. todos. firstIndex ( where: { $0. id == todo. id } ) {
205+ state. todos [ index] = todo
206+ }
207+ case . setLoading( let value) :
208+ state. isLoading = value
209+ case . setTodos( let todos) :
210+ state. todos = todos
211+ default :
212+ break
213+ }
214+ return [ ]
215+ }
216+ }
217+
218+ // MARK: - Helper Methods
188219private extension TodoViewModel {
220+ func setAlert(
221+ _ state: inout State ,
222+ isPresented: Bool
223+ ) {
224+ state. alertTitle = " 오류 "
225+ state. alertMessage = " 문제가 발생했습니다. 잠시 후 다시 시도해주세요. "
226+ state. showAlert = isPresented
227+ }
228+
189229 func setToast(
190230 _ state: inout State ,
191- isPresented: Bool ,
192- for type: ToastType ?
231+ isPresented: Bool
193232 ) {
194- switch type {
195- case . delete:
196- state. toastMessage = " 실행 취소 "
197- case . none:
198- state. toastMessage = " "
199- }
233+ state. toastMessage = " 실행 취소 "
200234 state. showToast = isPresented
201235 }
202236}
0 commit comments