Listless/Sync/CloudKitErrorClassifier.swift (1948B)
1import CloudKit
2import Foundation
3
4enum SyncIssue {
5 case transient(message: String)
6 case deferred(message: String)
7}
8
9enum CloudKitErrorClassifier {
10 static func classify(_ error: Error) -> SyncIssue {
11 let rootError = unwrap(error)
12 let nsError = rootError as NSError
13
14 if nsError.domain == CKError.errorDomain,
15 let ckCode = CKError.Code(rawValue: nsError.code)
16 {
17 return classifyCloudKit(code: ckCode)
18 }
19
20 return .transient(message: "Saved locally. iCloud sync will retry automatically.")
21 }
22
23 private static func classifyCloudKit(code: CKError.Code) -> SyncIssue {
24 switch code {
25 case .networkUnavailable, .networkFailure, .serviceUnavailable, .requestRateLimited,
26 .zoneBusy, .serverResponseLost, .operationCancelled:
27 return .transient(message: "Saved locally. iCloud sync will retry automatically.")
28
29 case .notAuthenticated:
30 return .transient(message: "Sign in to iCloud to sync across devices.")
31
32 case .quotaExceeded:
33 return .transient(message: "iCloud storage full. Free up space to continue syncing.")
34
35 case .permissionFailure, .badContainer, .missingEntitlement:
36 return .transient(message: "iCloud sync unavailable. Check iCloud settings.")
37
38 case .accountTemporarilyUnavailable, .zoneNotFound, .userDeletedZone:
39 return .deferred(message: "Saved locally. iCloud sync will retry automatically.")
40
41 default:
42 return .deferred(message: "Saved locally. iCloud sync will retry automatically.")
43 }
44 }
45
46 private static func unwrap(_ error: Error) -> Error {
47 if let storeError = error as? ItemStoreError {
48 switch storeError {
49 case .fetchFailed(let wrappedError), .saveFailed(let wrappedError):
50 return wrappedError
51 }
52 }
53 return error
54 }
55}