-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTodoViewModel.swift
More file actions
139 lines (127 loc) · 3.91 KB
/
Copy pathTodoViewModel.swift
File metadata and controls
139 lines (127 loc) · 3.91 KB
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//
// TodoViewModel.swift
// DevLog
//
// Created by 최윤진 on 11/22/25.
//
import Foundation
final class TodoViewModel: Store {
struct State {
var todos: [Todo] = []
var searchText: String = ""
let kind: TodoKind
var showEditor: Bool = false
var showAlert: Bool = false
var alertMessage: String = ""
var scope: TodoScope = .title
var filterOption: FilterOption = .create
var isLoading = false
}
enum FilterOption {
case create, update, day, week, month, year
}
enum Action {
// Modifier
case onAppear, refresh
// User
case tapTogglePinned(Todo)
case swipeTodo(Todo)
case tapFilterOption(FilterOption)
case upsertTodo(Todo)
// Binding
case openEditor
case closeEditor
case closeAlert
case setScope(TodoScope)
case setSearchText(String)
// Call from run
case didFetchTodos([Todo])
case didLoading(Bool)
case didShowAlert(String)
case didTogglePinned(Todo)
}
enum SideEffect {
case fetchTodos
case upsertTodo(Todo)
case togglePinned(Todo)
case swipeTodo(Todo)
}
private let fetchTodosByKindUseCase: FetchTodosByKindUseCase
private let upsertTodoUseCase: UpsertTodoUseCase
@Published private(set) var state: State
init(
fetchTodosByKindUseCase: FetchTodosByKindUseCase,
upsertTodoUseCase: UpsertTodoUseCase,
kind: TodoKind
) {
self.fetchTodosByKindUseCase = fetchTodosByKindUseCase
self.upsertTodoUseCase = upsertTodoUseCase
self.state = State(kind: kind)
}
func reduce(with action: Action) -> [SideEffect] {
switch action {
case .onAppear, .refresh:
return [.fetchTodos]
case .tapTogglePinned(let todo):
return [.togglePinned(todo)]
case .swipeTodo(let todo):
return [.swipeTodo(todo)]
case .tapFilterOption(let option):
state.filterOption = option
case .upsertTodo(let todo):
return [.upsertTodo(todo)]
case .openEditor:
state.showEditor = true
case .closeEditor:
state.showEditor = false
case .closeAlert:
state.showAlert = false
case .setScope(let scope):
state.scope = scope
case .setSearchText(let text):
state.searchText = text
case .didFetchTodos(let todos):
state.todos = todos
case .didLoading(let value):
state.isLoading = value
case .didShowAlert(let message):
state.alertMessage = message
state.showAlert = true
case .didTogglePinned(let todo):
if let index = state.todos.firstIndex(where: { $0.id == todo.id }) {
state.todos[index] = todo
}
}
return []
}
func run(_ effect: SideEffect) {
switch effect {
case .fetchTodos:
Task {
do {
defer { send(.didLoading(false)) }
send(.didLoading(true))
let todos = try await fetchTodosByKindUseCase.execute(state.kind)
send(.didFetchTodos(todos))
} catch {
send(.didShowAlert(error.localizedDescription))
}
}
case .upsertTodo(let todo):
Task {
do {
defer { send(.didLoading(false)) }
send(.didLoading(true))
try await upsertTodoUseCase.execute(todo)
send(.refresh)
} catch {
send(.didShowAlert(error.localizedDescription))
}
}
case .togglePinned(let todo):
break
case .swipeTodo(let todo):
break
}
}
}