Skip to content

Commit 0973070

Browse files
authored
[Feat] 네트워크 재시도 에러 View 구현 (#85)
1 parent 98fc1cc commit 0973070

45 files changed

Lines changed: 806 additions & 105 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Projects/DataSource/Sources/NetworkService/NetworkService.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,19 @@ final class NetworkService {
6060
}
6161
}
6262

63-
let (data, response) = try await URLSession.shared.data(for: request)
63+
let data: Data
64+
let response: URLResponse
65+
do {
66+
(data, response) = try await URLSession.shared.data(for: request)
67+
} catch let error as URLError {
68+
if [.notConnectedToInternet, .timedOut, .networkConnectionLost].contains(error.code) {
69+
throw NetworkError.needRetry
70+
} else {
71+
throw NetworkError.unknown(description: error.localizedDescription)
72+
}
73+
} catch {
74+
throw NetworkError.unknown(description: error.localizedDescription)
75+
}
6476

6577
if withPlugins {
6678
for plugin in plugins {

Projects/DataSource/Sources/Repository/EmotionRepository.swift

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,64 @@ final class EmotionRepository: EmotionRepositoryProtocol {
1212

1313
func fetchEmotions() async throws -> [EmotionEntity] {
1414
let endpoint = EmotionEndpoint.fetchEmotions
15-
guard let response = try await networkService.request(endpoint: endpoint, type: [EmotionResponseDTO].self)
16-
else { return [] }
1715

18-
let emotionEntities = response.compactMap({ $0.toEmotionEntity() })
19-
return emotionEntities
16+
do {
17+
guard let response = try await networkService.request(endpoint: endpoint, type: [EmotionResponseDTO].self)
18+
else { return [] }
19+
20+
let emotionEntities = response.compactMap({ $0.toEmotionEntity() })
21+
return emotionEntities
22+
} catch let error as NetworkError {
23+
switch error {
24+
case .needRetry, .invalidURL, .emptyData:
25+
throw DomainError.requireRetry
26+
default:
27+
throw DomainError.business(error.description)
28+
}
29+
} catch {
30+
throw DomainError.unknown
31+
}
2032
}
2133

2234
func loadEmotion(date: String) async throws -> EmotionEntity? {
2335
let endpoint = EmotionEndpoint.loadEmotion(date: date)
24-
guard let response = try await networkService.request(endpoint: endpoint, type: EmotionResponseDTO.self)
25-
else { throw NetworkError.unknown(description: "Emotion Reponse를 받아오지 못했습니다.") }
2636

27-
let emotionEntity = response.toEmotionEntity()
28-
return emotionEntity
37+
do {
38+
guard let response = try await networkService.request(endpoint: endpoint, type: EmotionResponseDTO.self)
39+
else { throw NetworkError.unknown(description: "Emotion Reponse를 받아오지 못했습니다.") }
40+
41+
let emotionEntity = response.toEmotionEntity()
42+
return emotionEntity
43+
} catch let error as NetworkError {
44+
switch error {
45+
case .needRetry, .invalidURL, .emptyData:
46+
throw DomainError.requireRetry
47+
default:
48+
throw DomainError.business(error.description)
49+
}
50+
} catch {
51+
throw DomainError.unknown
52+
}
2953
}
3054

3155
func registerEmotion(emotion: String) async throws -> [RecommendedRoutineEntity] {
3256
let endpoint = EmotionEndpoint.registerEmotion(emotion: emotion)
33-
guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineListResponseDTO.self)
34-
else { return [] }
3557

36-
let recommendedRoutineEntity = response.recommendedRoutines.compactMap({ $0.toRecommendedRoutineEntity() })
37-
return recommendedRoutineEntity
58+
do {
59+
guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineListResponseDTO.self)
60+
else { return [] }
61+
62+
let recommendedRoutineEntity = response.recommendedRoutines.compactMap({ $0.toRecommendedRoutineEntity() })
63+
return recommendedRoutineEntity
64+
} catch let error as NetworkError {
65+
switch error {
66+
case .needRetry, .invalidURL, .emptyData:
67+
throw DomainError.requireRetry
68+
default:
69+
throw DomainError.business(error.description)
70+
}
71+
} catch {
72+
throw DomainError.unknown
73+
}
3874
}
3975
}

Projects/DataSource/Sources/Repository/FileRepository.swift

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,34 @@ final class FileRepository: FileRepositoryProtocol {
1515
let dtos = fileNames.map { FilePresignedConditionDTO(prefix: prefix, fileName: $0) }
1616
let endpoint = FilePresignedEndpoint.fetchPresignedURL(presignedConditions: dtos)
1717

18-
return try await networkService.request(endpoint: endpoint, type: [String:String].self)
18+
do {
19+
return try await networkService.request(endpoint: endpoint, type: [String:String].self)
20+
} catch let error as NetworkError {
21+
switch error {
22+
case .needRetry, .invalidURL, .emptyData:
23+
throw DomainError.requireRetry
24+
default:
25+
throw DomainError.business(error.description)
26+
}
27+
} catch {
28+
throw DomainError.unknown
29+
}
1930
}
2031

2132
func uploadFile(url: String, data: Data) async throws {
2233
let endPoint = S3Endpoint.uploadImage(uploadURL: url, data: data)
23-
_ = try await networkService.request(endpoint: endPoint, type: EmptyResponseDTO.self)
34+
35+
do {
36+
_ = try await networkService.request(endpoint: endPoint, type: EmptyResponseDTO.self)
37+
} catch let error as NetworkError {
38+
switch error {
39+
case .needRetry, .invalidURL, .emptyData:
40+
throw DomainError.requireRetry
41+
default:
42+
throw DomainError.business(error.description)
43+
}
44+
} catch {
45+
throw DomainError.unknown
46+
}
2447
}
2548
}

Projects/DataSource/Sources/Repository/LocationRepository.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,22 @@ final class LocationRepository: NSObject, LocationRepositoryProtocol {
4444

4545
let endpoint = LocationEndpoint.fetchAddress(longitude: longitude, latitude: latitude)
4646

47-
guard let response = try await networkService.request(endpoint: endpoint, type: KakaoLocationResponseDTO.self)
48-
else { return nil }
47+
do {
48+
guard let response = try await networkService.request(endpoint: endpoint, type: KakaoLocationResponseDTO.self)
49+
else { return nil }
4950

50-
let location = response.toLocationEntity(fallbackLongitude: coordinate.longitude, fallbackLatitude: coordinate.latitude)
51-
return location
51+
let location = response.toLocationEntity(fallbackLongitude: coordinate.longitude, fallbackLatitude: coordinate.latitude)
52+
return location
53+
} catch let error as NetworkError {
54+
switch error {
55+
case .needRetry, .invalidURL, .emptyData:
56+
throw DomainError.requireRetry
57+
default:
58+
throw DomainError.business(error.description)
59+
}
60+
} catch {
61+
throw DomainError.unknown
62+
}
5263
}
5364

5465
private func requestAuthorizationIfNeeded() async -> CLAuthorizationStatus {

Projects/DataSource/Sources/Repository/OnboardingRepository.swift

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,21 @@ final class OnboardingRepository: OnboardingRepositoryProtocol {
1313

1414
func loadOnboardingResult() async throws -> OnboardingEntity {
1515
let endpoint = OnboardingEndpoint.loadOnboardingResult
16-
guard let response = try await networkService.request(endpoint: endpoint, type: OnboardingResponseDTO.self)
17-
else { throw UserError.onboardingLoadFailed }
1816

19-
let onboardingEntity = response.toOnboardingEntity()
20-
return onboardingEntity
17+
do {
18+
guard let response = try await networkService.request(endpoint: endpoint, type: OnboardingResponseDTO.self)
19+
else { throw UserError.onboardingLoadFailed }
20+
21+
let onboardingEntity = response.toOnboardingEntity()
22+
return onboardingEntity
23+
} catch let error as NetworkError {
24+
switch error {
25+
case .needRetry, .invalidURL, .emptyData:
26+
throw DomainError.requireRetry
27+
default:
28+
throw DomainError.business(error.description)
29+
}
30+
}
2131
}
2232

2333
func registerOnboarding(onboardingEntity: OnboardingEntity) async throws -> [RecommendedRoutineEntity] {
@@ -27,15 +37,39 @@ final class OnboardingRepository: OnboardingRepositoryProtocol {
2737
realOutingFrequency: onboardingEntity.frequency,
2838
targetOutingFrequency: onboardingEntity.outdoor)
2939
let endpoint = OnboardingEndpoint.registerOnboarding(onboarding: onboardingDTO)
30-
guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineListResponseDTO.self)
31-
else { return [] }
32-
33-
let recommendedRoutineEntity = response.recommendedRoutines.compactMap({ $0.toRecommendedRoutineEntity() })
34-
return recommendedRoutineEntity
40+
41+
do {
42+
guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineListResponseDTO.self)
43+
else { return [] }
44+
45+
let recommendedRoutineEntity = response.recommendedRoutines.compactMap({ $0.toRecommendedRoutineEntity() })
46+
return recommendedRoutineEntity
47+
} catch let error as NetworkError {
48+
switch error {
49+
case .needRetry, .invalidURL, .emptyData:
50+
throw DomainError.requireRetry
51+
default:
52+
throw DomainError.business(error.description)
53+
}
54+
} catch {
55+
throw DomainError.unknown
56+
}
3557
}
3658

3759
func registerRecommendedRoutines(selectedRoutines: [Int]) async throws {
3860
let endpoint = OnboardingEndpoint.registerRecommendedRoutine(selectedRoutines: selectedRoutines)
39-
_ = try await networkService.request(endpoint: endpoint, type: EmptyResponseDTO.self)
61+
62+
do {
63+
_ = try await networkService.request(endpoint: endpoint, type: EmptyResponseDTO.self)
64+
} catch let error as NetworkError {
65+
switch error {
66+
case .needRetry, .invalidURL, .emptyData:
67+
throw DomainError.requireRetry
68+
default:
69+
throw DomainError.business(error.description)
70+
}
71+
} catch {
72+
throw DomainError.unknown
73+
}
4074
}
4175
}

Projects/DataSource/Sources/Repository/RecommendedRoutineRepository.swift

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,46 @@ final class RecommendedRoutineRepository: RecommendedRoutineRepositoryProtocol {
1313
func fetchRecommendedRoutine(id: Int) async throws -> RecommendedRoutineEntity? {
1414
let endpoint = RecommendedRoutineEndpoint.fetchRecommendedRoutine(id: id)
1515

16-
guard let recommendedRoutineDTO = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineDTO.self)
17-
else { return nil }
18-
19-
return recommendedRoutineDTO.toRecommendedRoutineEntity()
16+
do {
17+
guard let recommendedRoutineDTO = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineDTO.self)
18+
else { return nil }
19+
20+
return recommendedRoutineDTO.toRecommendedRoutineEntity()
21+
} catch let error as NetworkError {
22+
switch error {
23+
case .needRetry, .invalidURL, .emptyData:
24+
throw DomainError.requireRetry
25+
default:
26+
throw DomainError.business(error.description)
27+
}
28+
} catch {
29+
throw DomainError.unknown
30+
}
2031
}
2132

2233
func fetchRecommendedRoutines() async throws -> [RecommendedRoutineEntity] {
2334
let endpoint = RecommendedRoutineEndpoint.fetchRecommendedRoutines
24-
guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineDictionaryResponseDTO.self)
25-
else { return [] }
2635

27-
var entities: [RecommendedRoutineEntity] = []
28-
for (category, recommendedRoutines) in response.recommendedRoutines {
29-
let recommendedRoutineEntity = recommendedRoutines.compactMap({ $0.toRecommendedRoutineEntity(category: category) })
30-
entities.append(contentsOf: recommendedRoutineEntity)
36+
do {
37+
guard let response = try await networkService.request(endpoint: endpoint, type: RecommendedRoutineDictionaryResponseDTO.self)
38+
else { return [] }
39+
40+
var entities: [RecommendedRoutineEntity] = []
41+
for (category, recommendedRoutines) in response.recommendedRoutines {
42+
let recommendedRoutineEntity = recommendedRoutines.compactMap({ $0.toRecommendedRoutineEntity(category: category) })
43+
entities.append(contentsOf: recommendedRoutineEntity)
44+
}
45+
46+
return entities
47+
} catch let error as NetworkError {
48+
switch error {
49+
case .needRetry, .invalidURL, .emptyData:
50+
throw DomainError.requireRetry
51+
default:
52+
throw DomainError.business(error.description)
53+
}
54+
} catch {
55+
throw DomainError.unknown
3156
}
32-
33-
return entities
3457
}
3558
}

Projects/DataSource/Sources/Repository/ReportRepository.swift

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,66 @@ final class ReportRepository: ReportRepositoryProtocol {
3333
)
3434

3535
let endpoint = ReportEndpoint.register(report: reportDTO)
36-
guard let id = try await networkService.request(endpoint: endpoint, type: Int.self) else { return nil }
3736

38-
return id
37+
do {
38+
guard let id = try await networkService.request(endpoint: endpoint, type: Int.self) else { return nil }
39+
40+
return id
41+
} catch let error as NetworkError {
42+
switch error {
43+
case .needRetry, .invalidURL, .emptyData:
44+
throw DomainError.requireRetry
45+
default:
46+
throw DomainError.business(error.description)
47+
}
48+
} catch {
49+
throw DomainError.unknown
50+
}
3951
}
4052

4153
func fetchReports() async throws -> [ReportEntity] {
4254
let endpoint = ReportEndpoint.fetchReports
43-
guard let response = try await networkService.request(endpoint: endpoint, type: ReportDictonaryDTO.self)
44-
else { return [] }
4555

46-
var reportEntities: [ReportEntity] = []
47-
for (date, reports) in response.reportInfos {
48-
let reportHistories = reports.compactMap({ try? $0.toReportEntity(date: date) })
49-
reportEntities += reportHistories
50-
}
56+
do {
57+
guard let response = try await networkService.request(endpoint: endpoint, type: ReportDictonaryDTO.self)
58+
else { return [] }
59+
60+
var reportEntities: [ReportEntity] = []
61+
for (date, reports) in response.reportInfos {
62+
let reportHistories = reports.compactMap({ try? $0.toReportEntity(date: date) })
63+
reportEntities += reportHistories
64+
}
5165

52-
return reportEntities
66+
return reportEntities
67+
} catch let error as NetworkError {
68+
switch error {
69+
case .needRetry, .invalidURL, .emptyData:
70+
throw DomainError.requireRetry
71+
default:
72+
throw DomainError.business(error.description)
73+
}
74+
} catch {
75+
throw DomainError.unknown
76+
}
5377
}
5478

5579
func fetchReportDetail(reportId: Int) async throws -> ReportEntity? {
5680
let endpoint = ReportEndpoint.fetchReportDetail(reportId: reportId)
57-
guard let response = try await networkService.request(endpoint: endpoint, type: ReportDTO.self)
58-
else { return nil }
5981

60-
return try response.toReportEntity()
82+
do {
83+
guard let response = try await networkService.request(endpoint: endpoint, type: ReportDTO.self)
84+
else { return nil }
85+
86+
return try response.toReportEntity()
87+
} catch let error as NetworkError {
88+
switch error {
89+
case .needRetry, .invalidURL, .emptyData:
90+
throw DomainError.requireRetry
91+
default:
92+
throw DomainError.business(error.description)
93+
}
94+
} catch {
95+
throw DomainError.unknown
96+
}
6197
}
6298
}

0 commit comments

Comments
 (0)