Skip to content

Commit 423746b

Browse files
authored
[#71] 얼럿 메시지를 직접적인 에러 구문 대신 다른 문구로 대체한다 (#92)
* feat: ProfileView에 얼럿 띄우기 * refactor: PushNotificationSettingsView에 얼럿 띄우기 * refactor: SearchView에 얼럿 띄우기 * refactor: SettingView에 얼럿 띄우기 * style: 미사용 메서드 제거
1 parent 0b0dd55 commit 423746b

10 files changed

Lines changed: 195 additions & 118 deletions

DevLog/Presentation/ViewModel/ProfileViewModel.swift

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,23 @@ final class ProfileViewModel: Store {
1313
var email: String = ""
1414
var statusMessage: String = ""
1515
var avatarURL: URL?
16-
1716
var showDoneButton: Bool = false
18-
var showToast: Bool = false
19-
var toastMessage: String = ""
17+
var showAlert: Bool = false
18+
var alertTitle: String = ""
19+
var alertMessage: String = ""
2020
var resetButtonEnabled: Bool {
2121
!statusMessage.isEmpty && showDoneButton
2222
}
2323
}
2424

2525
enum Action {
2626
case onAppear
27-
case tapConfirmButton
27+
case setAlert(Bool)
2828
case tapResetStatusMessageButton
2929
case willUpdateStatusMessage
3030
case fetchUserData(UserProfile)
3131
case updateStatusMessage(String)
3232
case updateStatusTextFieldFocus(Bool)
33-
case tapCloseToast
3433
}
3534

3635
enum SideEffect {
@@ -52,11 +51,12 @@ final class ProfileViewModel: Store {
5251

5352
func reduce(with action: Action) -> [SideEffect] {
5453
var state = self.state
54+
var effects: [SideEffect] = []
5555
switch action {
5656
case .onAppear:
57-
return [.fetchUserData]
58-
case .tapConfirmButton:
59-
state.showToast = false
57+
effects = [.fetchUserData]
58+
case .setAlert(let isPresented):
59+
setAlert(&state, isPresented: isPresented)
6060
case .tapResetStatusMessageButton:
6161
state.statusMessage = ""
6262
case .fetchUserData(let profile):
@@ -66,29 +66,46 @@ final class ProfileViewModel: Store {
6666
state.avatarURL = profile.avatarURL
6767
case .willUpdateStatusMessage:
6868
let message = self.state.statusMessage
69-
return [.updateStatusMessage(message)]
69+
effects = [.updateStatusMessage(message)]
7070
case .updateStatusMessage(let message):
7171
state.statusMessage = message
7272
case .updateStatusTextFieldFocus(let focused):
7373
state.showDoneButton = focused
74-
case .tapCloseToast:
75-
state.showToast = false
7674
}
7775
self.state = state
78-
return []
76+
return effects
7977
}
8078

8179
func run(_ effect: SideEffect) {
8280
switch effect {
8381
case .fetchUserData:
8482
Task {
85-
let profile = try await fetchUserDataUseCase.execute()
86-
send(.fetchUserData(profile))
83+
do {
84+
let profile = try await fetchUserDataUseCase.execute()
85+
send(.fetchUserData(profile))
86+
} catch {
87+
send(.setAlert(true))
88+
}
8789
}
8890
case .updateStatusMessage(let message):
8991
Task {
90-
try await upsertStatusMessageUseCase.execute(message)
92+
do {
93+
try await upsertStatusMessageUseCase.execute(message)
94+
} catch {
95+
send(.setAlert(true))
96+
}
9197
}
9298
}
9399
}
94100
}
101+
102+
private extension ProfileViewModel {
103+
func setAlert(
104+
_ state: inout State,
105+
isPresented: Bool
106+
) {
107+
state.alertTitle = "오류"
108+
state.alertMessage = "문제가 발생했습니다. 잠시 후 다시 시도해주세요."
109+
state.showAlert = isPresented
110+
}
111+
}

DevLog/Presentation/ViewModel/PushNotificationSettingsViewModel.swift

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@ import Foundation
99

1010
final class PushNotificationSettingsViewModel: Store {
1111
struct State {
12-
var pushNotificationEnable = false
13-
var pushNotificationTime = Date()
14-
var showTimePicker = false
15-
var sheetHeight = CGFloat.pi
12+
var pushNotificationEnable: Bool = false
13+
var pushNotificationTime: Date = .init()
14+
var showTimePicker: Bool = false
15+
var isLoading: Bool = false
16+
var sheetHeight: CGFloat = .pi
17+
var showSheet: Bool = false
18+
var showAlert: Bool = false
19+
var alertTitle: String = ""
20+
var alertMessage: String = ""
1621
var pushNotificationHour: Int {
1722
Calendar.current.component(.hour, from: pushNotificationTime)
1823
}
@@ -23,6 +28,8 @@ final class PushNotificationSettingsViewModel: Store {
2328

2429
enum Action {
2530
case onAppear
31+
case setAlert(Bool)
32+
case setLoading(Bool)
2633
case setPushNotificationEnable(Bool)
2734
case setPushNotificationHour(Int)
2835
case setPushNotificationTime(Date)
@@ -53,6 +60,10 @@ final class PushNotificationSettingsViewModel: Store {
5360
switch action {
5461
case .onAppear:
5562
return [.fetchPushNotificationSettings]
63+
case .setAlert(let isPresented):
64+
setAlert(&state, isPresented: isPresented)
65+
case .setLoading(let value):
66+
state.isLoading = value
5667
case .setPushNotificationEnable(let value):
5768
self.state.pushNotificationEnable = value
5869
return [.updatePushNotificationSettings]
@@ -82,24 +93,46 @@ final class PushNotificationSettingsViewModel: Store {
8293
switch effect {
8394
case .fetchPushNotificationSettings:
8495
Task {
85-
let settings = try await fetchPushSettingsUseCase.execute()
86-
self.send(.setPushNotificationEnable(settings.isEnabled))
87-
if let hour = settings.scheduledTime.hour,
88-
let minute = settings.scheduledTime.minute,
89-
let date = calendar.date(bySettingHour: hour, minute: minute, second: 0, of: Date()) {
90-
self.send(.setPushNotificationTime(date))
96+
do {
97+
defer { send(.setLoading(false)) }
98+
send(.setLoading(true))
99+
let settings = try await fetchPushSettingsUseCase.execute()
100+
self.send(.setPushNotificationEnable(settings.isEnabled))
101+
if let hour = settings.scheduledTime.hour,
102+
let minute = settings.scheduledTime.minute,
103+
let date = calendar.date(bySettingHour: hour, minute: minute, second: 0, of: Date()) {
104+
self.send(.setPushNotificationTime(date))
105+
}
106+
} catch {
107+
send(.setAlert(true))
91108
}
92109
}
93110
case .updatePushNotificationSettings:
94111
Task {
95-
let dateComponents = calendar.dateComponents([.hour, .minute], from: state.pushNotificationTime)
96-
let settings = PushNotificationSettings(
97-
isEnabled: state.pushNotificationEnable,
98-
scheduledTime: dateComponents
99-
)
100-
101-
try await updatePushSettingsUseCase.execute(settings)
112+
do {
113+
defer { send(.setLoading(false)) }
114+
send(.setLoading(true))
115+
let dateComponents = calendar.dateComponents([.hour, .minute], from: state.pushNotificationTime)
116+
let settings = PushNotificationSettings(
117+
isEnabled: state.pushNotificationEnable,
118+
scheduledTime: dateComponents
119+
)
120+
try await updatePushSettingsUseCase.execute(settings)
121+
} catch {
122+
send(.setAlert(true))
123+
}
102124
}
103125
}
104126
}
105127
}
128+
129+
extension PushNotificationSettingsViewModel {
130+
func setAlert(
131+
_ state: inout State,
132+
isPresented: Bool
133+
) {
134+
state.alertTitle = "오류"
135+
state.alertMessage = "문제가 발생했습니다. 잠시 후 다시 시도해주세요."
136+
state.showAlert = isPresented
137+
}
138+
}

DevLog/Presentation/ViewModel/SearchViewModel.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ final class SearchViewModel: Store {
2121
$0.displayURL.localizedCaseInsensitiveContains(searchQuery)
2222
}
2323
}
24+
var showAlert: Bool = false
25+
var alertTitle: String = ""
26+
var alertMessage: String = ""
2427
}
2528

2629
enum Action {
2730
case onAppear
2831
case fetchWebPage([WebPageItem]? = nil)
2932
case selectWebPage(WebPageItem)
33+
case setAlert(Bool)
3034
case setLoading(Bool)
3135
case setSearching(Bool)
3236
case setSearchQuery(String)
@@ -59,6 +63,8 @@ final class SearchViewModel: Store {
5963
state.webPages = OrderedSet(items)
6064
case .selectWebPage(let item):
6165
state.selectedWebPage = item
66+
case .setAlert(let isPresented):
67+
setAlert(&state, isPresented: isPresented)
6268
case .setLoading(let isLoading):
6369
state.isLoading = isLoading
6470
case .setSearching(let isSearching):
@@ -81,9 +87,20 @@ final class SearchViewModel: Store {
8187
let items = try await self.fetchWebPagesUseCase.execute().map { WebPageItem(from: $0) }
8288
send(.fetchWebPage(items))
8389
} catch {
84-
90+
send(.setAlert(true))
8591
}
8692
}
8793
}
8894
}
8995
}
96+
97+
private extension SearchViewModel {
98+
func setAlert(
99+
_ state: inout State,
100+
isPresented: Bool
101+
) {
102+
state.alertTitle = "오류"
103+
state.alertMessage = "문제가 발생했습니다. 잠시 후 다시 시도해주세요."
104+
state.showAlert = isPresented
105+
}
106+
}

DevLog/Presentation/ViewModel/SettingViewModel.swift

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,32 @@ import Foundation
1010
final class SettingViewModel: Store {
1111
struct State {
1212
var theme = ""
13-
var showDeleteUserAlert = false
14-
var showSignOutAlert = false
15-
var toastMessage = ""
16-
var showToast = false
1713
var isLoading = false
14+
var showAlert: Bool = false
15+
var alertTitle: String = ""
16+
var alertType: AlertType?
17+
var alertMessage: String = ""
1818
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
1919
let policyURL = Bundle.main.object(forInfoDictionaryKey: "PRIVACY_POLICY_URL") as? String
2020
}
2121

2222
enum Action {
23-
case toggleToast(Bool)
23+
case setAlert(isPresented: Bool, type: AlertType? = nil)
2424
case setLoading(Bool)
2525
case setTheme(String)
26-
case setToastMessage(String)
2726
case tapDeleteAuthButton
2827
case tapSignOutButton
29-
case toggleDeleteUserAlert(Bool)
30-
case toggleSignOutAlert(Bool)
3128
}
3229

3330
enum SideEffect {
3431
case deleteAuth
3532
case signOut
3633
}
3734

35+
enum AlertType {
36+
case signOut, delete, error
37+
}
38+
3839
private let deleteAuthuseCase: DeleteAuthUseCase
3940
private let signOutUseCase: SignOutUseCase
4041
private let sessionUseCase: AuthSessionUseCase
@@ -53,22 +54,16 @@ final class SettingViewModel: Store {
5354

5455
func reduce(with action: Action) -> [SideEffect] {
5556
switch action {
56-
case .toggleToast(let value):
57-
state.showToast = value
57+
case .setAlert(let isPresented, let type):
58+
setAlert(&state, isPresented: isPresented, type: type)
5859
case .setLoading(let value):
5960
state.isLoading = value
6061
case .setTheme(let value):
6162
state.theme = value
62-
case .setToastMessage(let message):
63-
state.toastMessage = message
6463
case .tapDeleteAuthButton:
65-
break
64+
return [.deleteAuth]
6665
case .tapSignOutButton:
6766
return [.signOut]
68-
case .toggleDeleteUserAlert(let value):
69-
state.showDeleteUserAlert = value
70-
case .toggleSignOutAlert(let value):
71-
state.showSignOutAlert = value
7267
}
7368
return []
7469
}
@@ -79,27 +74,50 @@ final class SettingViewModel: Store {
7974
Task {
8075
do {
8176
defer { send(.setLoading(false)) }
82-
send(.toggleDeleteUserAlert(false))
77+
send(.setAlert(isPresented: false))
8378
send(.setLoading(true))
8479
try await deleteAuthuseCase.execute()
8580
} catch {
86-
send(.toggleToast(true))
87-
send(.setToastMessage(error.localizedDescription))
81+
send(.setAlert(isPresented: true, type: .error))
8882
}
8983
}
9084
case .signOut:
9185
Task {
9286
do {
9387
defer { send(.setLoading(false)) }
94-
send(.toggleSignOutAlert(false))
88+
send(.setAlert(isPresented: false))
9589
send(.setLoading(true))
9690
try await signOutUseCase.execute()
9791
sessionUseCase.execute(false)
9892
} catch {
99-
send(.toggleToast(true))
100-
send(.setToastMessage(error.localizedDescription))
93+
send(.setAlert(isPresented: true, type: .error))
10194
}
10295
}
10396
}
10497
}
10598
}
99+
100+
private extension SettingViewModel {
101+
func setAlert(
102+
_ state: inout State,
103+
isPresented: Bool,
104+
type: AlertType?
105+
) {
106+
switch type {
107+
case .signOut:
108+
state.alertTitle = "로그아웃"
109+
state.alertMessage = "로그아웃 하시겠습니까?"
110+
case .delete:
111+
state.alertTitle = "정말 탈퇴하시겠습니까?"
112+
state.alertMessage = "회원 탈퇴가 진행되면 모든 데이터가 지워지고 복구할 수 없습니다."
113+
case .error:
114+
state.alertTitle = "오류"
115+
state.alertMessage = "문제가 발생했습니다. 잠시 후 다시 시도해주세요."
116+
case .none:
117+
state.alertTitle = ""
118+
state.alertMessage = ""
119+
}
120+
state.showAlert = isPresented
121+
state.alertType = type
122+
}
123+
}

DevLog/Presentation/ViewModel/TodoEditorViewModel.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,6 @@ final class TodoEditorViewModel: Store {
104104
}
105105
return []
106106
}
107-
108-
func run(_ effect: SideEffect) { }
109107
}
110108

111109
extension TodoEditorViewModel {

0 commit comments

Comments
 (0)