Skip to content

Commit

Permalink
Merge pull request #16 from innovationacademy-kr/feat/errorHandler/2
Browse files Browse the repository at this point in the history
FEAT: 상황에 따른 에러 핸들링
  • Loading branch information
ittzggd authored Jun 7, 2024
2 parents b07d026 + 0791aa8 commit 9e907aa
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 15 deletions.
8 changes: 8 additions & 0 deletions HANE24.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
0E9868252B2B6C0B00E127DC /* CalendarHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9868242B2B6C0B00E127DC /* CalendarHeaderView.swift */; };
0E9868272B2B76BE00E127DC /* CalendarBodyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9868262B2B76BE00E127DC /* CalendarBodyView.swift */; };
0EBBF14E29B08D580076AAB9 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EBBF14D29B08D580076AAB9 /* ErrorView.swift */; };
0ECF97A62BEDE7CB00C37095 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECF97A52BEDE7CB00C37095 /* Error.swift */; };
0ECF97A82BEDE8EA00C37095 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECF97A72BEDE8EA00C37095 /* ErrorHandler.swift */; };
0ED6E3E32B354D320026E69D /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED6E3E22B354D310026E69D /* WidgetKit.framework */; };
0ED6E3E52B354D320026E69D /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED6E3E42B354D320026E69D /* SwiftUI.framework */; };
0ED6E3E82B354D320026E69D /* HANE24WidgetBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED6E3E72B354D320026E69D /* HANE24WidgetBundle.swift */; };
Expand Down Expand Up @@ -134,6 +136,8 @@
0E9868242B2B6C0B00E127DC /* CalendarHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarHeaderView.swift; sourceTree = "<group>"; };
0E9868262B2B76BE00E127DC /* CalendarBodyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarBodyView.swift; sourceTree = "<group>"; };
0EBBF14D29B08D580076AAB9 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
0ECF97A52BEDE7CB00C37095 /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = "<group>"; };
0ECF97A72BEDE8EA00C37095 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
0ED6E3E12B354D310026E69D /* HANE24WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = HANE24WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
0ED6E3E22B354D310026E69D /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
0ED6E3E42B354D320026E69D /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -360,6 +364,7 @@
isa = PBXGroup;
children = (
9AF730F5299E074800AF2E53 /* Structs.swift */,
0ECF97A52BEDE7CB00C37095 /* Error.swift */,
9AF730FC299F602D00AF2E53 /* JSONs.swift */,
D68CC3E72BCC1764008D62E2 /* ReissueModel.swift */,
D6E780C82BCCE84300FB547D /* CalendarModel.swift */,
Expand All @@ -375,6 +380,7 @@
D6C528F72BBECE0700F51A06 /* ReissueVM.swift */,
D6185AE02BB3C71800E6944A /* CalendarVM.swift */,
0E3ED8A72BB13727001B0BAE /* NetworkManager.swift */,
0ECF97A72BEDE8EA00C37095 /* ErrorHandler.swift */,
0EE06CBC2BB2819500B4988C /* HomeVM.swift */,
0E6B608E29AC850D009D8BC4 /* NetworkMonitoringManager.swift */,
);
Expand Down Expand Up @@ -628,6 +634,7 @@
D6E780CE2BCD030200FB547D /* Tapbar.swift in Sources */,
D68CC3F32BCC1F2A008D62E2 /* ListItemButton.swift in Sources */,
9AF730FD299F602D00AF2E53 /* JSONs.swift in Sources */,
0ECF97A82BEDE8EA00C37095 /* ErrorHandler.swift in Sources */,
D6E780D42BCD04AB00FB547D /* ReissueButton.swift in Sources */,
D6AECA262BECCBF5009A8018 /* AccTimeCardsView.swift in Sources */,
0EE58193299CC24000EE3351 /* MoreView.swift in Sources */,
Expand Down Expand Up @@ -670,6 +677,7 @@
D6C528F82BBECE0700F51A06 /* ReissueVM.swift in Sources */,
D6E19CF32BEB427B005DF8C3 /* Error.swift in Sources */,
9AF730F3299D599300AF2E53 /* TagLogView.swift in Sources */,
0ECF97A62BEDE7CB00C37095 /* Error.swift in Sources */,
0E0FE5F32BB3E6110050498E /* CircularProgressBar.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
1 change: 0 additions & 1 deletion HANE24/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ struct ContentView: View {
UserDefaults.standard.setValue(0, forKey: "MonthlySelectionOption")
UserDefaults.standard.set(true, forKey: "isFirst")
}

do {
try hane.isSignIn = await hane.isLogin() ? true : false
self.signInChecked = true
Expand Down
65 changes: 65 additions & 0 deletions HANE24/Model/Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,71 @@

import Foundation

enum CustomError: Error {
case tokenExpired
case wrongQueryType
case networkDisconnected
case unAuthorized
case internalServer
case responseBodyEmpty
case decodeFailed
case invalidURL
case unknownError(String)
case none
}

extension CustomError: LocalizedError {
public var errorDescription: String? {
switch self {
case .tokenExpired:
return "사용자 토큰이 만료되었습니다"
case .wrongQueryType:
return "잘못된 요청입니다"
case .networkDisconnected:
return "네트워크 상태가 원활하지 않습니다"
case .unAuthorized:
return "알 수 없는 사용자입니다"
case .internalServer:
return "서버 에러 발생"
case .responseBodyEmpty:
return "내부 에러 발생"
case .decodeFailed:
return "내부 에러 발생"
case .invalidURL:
return "잘못된 접근입니다"
case .unknownError:
return "원인을 알 수 없는 에러 발생"
case .none:
return nil
}
}

public var recoverySuggestion: String? {
switch self {
case .tokenExpired:
return "다시 로그인해주세요"
case .wrongQueryType:
return "다시 시도해주세요"
case .networkDisconnected:
return "Wi-Fi 혹은 데이터 확인 후 다시 시도해주세요"
case .unAuthorized:
return "로그인 정보를 다시 확인해주세요"
case .internalServer:
return "개발팀에게 문의해주세요"
case .responseBodyEmpty:
return "개발팀에게 문의해주세요"
case .decodeFailed:
return "개발팀에게 문의해주세요"
case .invalidURL:
return "개발팀에게 문의해주세요"
case .unknownError:
return "개발팀에게 문의해주세요"
case .none:
return nil
}
}
}

// API 처리에 대한 실패
enum ReissueError: Error {
case tokenExpired(String)
Expand Down
11 changes: 11 additions & 0 deletions HANE24/View/MainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SwiftUI
struct MainView: View {
@EnvironmentObject var hane: Hane
@StateObject var homeVM = HomeVM()
@ObservedObject var errorHandler = ErrorHandler.shared

@State var selection = 1
@Environment(\.colorScheme) var colorScheme
Expand Down Expand Up @@ -41,6 +42,7 @@ struct MainView: View {
try await homeVM.refresh()
} catch {
print("error on MainView \(error.localizedDescription)")
print("error: ", error)
}
}

Expand All @@ -50,6 +52,15 @@ struct MainView: View {
NoticeView(showNotice: $isNoticedTagLatencyInfo, notice: hane.tagLatencyNotice)
}
}
.alert(
"에러가 발생했어요",
isPresented: $errorHandler.showAlert) {
Button("확인") {
errorHandler.errorType = CustomError.none
}
} message: {
Text(errorHandler.errorType.recoverySuggestion ?? "개발팀에 문의해주세요")
}
}
}

Expand Down
71 changes: 71 additions & 0 deletions HANE24/ViewModel/ErrorHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// ErrorHandler.swift
// 24HANE
//
// Created by Katherine JANG on 5/10/24.
//

import Foundation
import SwiftUI


class ErrorHandler: ObservableObject {

static let shared = ErrorHandler()

var errorType: CustomError = .none
@Published var showAlert: Bool = false
@Published var signInRequired: Bool = false

private init() { }

func errorFromHttpRequest(_ statusCode: Int?) throws {
switch statusCode {
case 400:
throw CustomError.wrongQueryType
case 401:
throw CustomError.unAuthorized
case 500:
throw CustomError.internalServer
default:
throw CustomError.unknownError("\(statusCode)")

Check warning on line 31 in HANE24/ViewModel/ErrorHandler.swift

View workflow job for this annotation

GitHub Actions / Test

string interpolation produces a debug description for an optional value; did you mean to make this explicit?
}
}

@MainActor
func updateErrorView() {
switch self.errorType {
case .tokenExpired, .unAuthorized:
self.signInRequired = true
case .wrongQueryType, .networkDisconnected, .internalServer, .responseBodyEmpty,
.decodeFailed, .unknownError, .invalidURL:
self.showAlert = true
case .none:
break
}
}

func verifyError(_ error: Error) async {
switch error {
case DecodingError.dataCorrupted:
self.errorType = .decodeFailed
case URLError.timedOut:
self.errorType = .networkDisconnected
case URLError.networkConnectionLost:
self.errorType = .networkDisconnected
case is CustomError:
self.errorType = error as? CustomError ?? .none
default:
self.errorType = .unknownError(error.localizedDescription.description)
}
}

Check warning on line 62 in HANE24/ViewModel/ErrorHandler.swift

View workflow job for this annotation

GitHub Actions / Test

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
@MainActor
func handleError(_ error: Error) {
Task {
await self.verifyError(error)
self.updateErrorView()
}
}

}
37 changes: 23 additions & 14 deletions HANE24/ViewModel/HomeVM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,38 @@ class HomeVM: ObservableObject {
}

@MainActor
func updateAccumulationTimes() async throws {
guard let accTimes = try await NetworkManager.shared.apiRequest("/v3/tag-log/accumulationTimes", .get, type: AccumulationTimes.self) else {
func updateAccumulationTimes() async {
do {
guard let accTimes = try await NetworkManager.shared.apiRequest("/v3/tag-log/accumulationTimes", .get, type: AccumulationTimes.self) else {
throw MyError.tokenExpired("")
}
self.accumulationTimes = accTimes
self.dailyAccumulationTime = accTimes.todayAccumulationTime
} catch {
ErrorHandler.shared.handleError(error)
}
self.accumulationTimes = accTimes
self.dailyAccumulationTime = accTimes.todayAccumulationTime
}

//TODO: 요청한 데이터가 nil일 경우 에러 핸들링

Check warning on line 64 in HANE24/ViewModel/HomeVM.swift

View workflow job for this annotation

GitHub Actions / Test

Comment Spacing Violation: Prefer at least one space after slashes for comments (comment_spacing)
@MainActor
func updateMainInfo() async throws {
guard let mainInfo = try await NetworkManager.shared.apiRequest("/v3/tag-log/maininfo", .get, type: MainInfo.self) else {
throw MyError.tokenExpired("")
}
self.mainInfo = mainInfo
self.isInCluster = mainInfo.inoutState == "IN"
self.fundInfoNotice = mainInfo.infoMessages.fundInfoNotice
self.tagLatencyNotice = mainInfo.infoMessages.tagLatencyNotice
func updateMainInfo() async {
do {
guard let mainInfo = try await NetworkManager.shared.apiRequest("/v3/tag-log/maininfo", .get, type: MainInfo.self) else {
throw MyError.tokenExpired("")
}
self.mainInfo = mainInfo
self.isInCluster = mainInfo.inoutState == "IN"
self.fundInfoNotice = mainInfo.infoMessages.fundInfoNotice
self.tagLatencyNotice = mainInfo.infoMessages.tagLatencyNotice
} catch {
print("error caught")
ErrorHandler.shared.handleError(error)
}
}

@MainActor
func refresh() async throws {
try await self.updateMainInfo()
try await self.updateAccumulationTimes()
await self.updateMainInfo()
await self.updateAccumulationTimes()
}
}

0 comments on commit 9e907aa

Please sign in to comment.