@@ -42,40 +42,47 @@ extension CloudKitError {
4242 /// models a CloudKit `serverErrorCode`; `nil` for all other cases.
4343 ///
4444 /// The switch is deliberately exhaustive: adding a case to ``CloudKitError``
45- /// stops compiling here until the new case is classified.
46- // swiftlint:disable:next cyclomatic_complexity function_body_length
45+ /// stops compiling here until the new case is classified. Wire strings and
46+ /// status numbers come from ``CloudKitServerErrorCode``'s catalog — never
47+ /// inlined here.
48+ // swiftlint:disable:next cyclomatic_complexity
4749 internal var serverErrorDetail : ServerErrorCodeDetail ? {
4850 switch self {
4951 case . accessDenied( let reason) :
50- return Self . detail ( " ACCESS_DENIED " , 403 , " access denied " , reason)
52+ return ServerErrorCodeDetail ( code : . accessDenied , reason : reason)
5153 case . atomicFailure( let reason) :
52- return Self . detail ( " ATOMIC_ERROR " , 400 , " atomic batch failure " , reason)
54+ return ServerErrorCodeDetail ( code : . atomicError , reason : reason)
5355 case . authenticationFailed( let reason) :
54- return Self . detail ( " AUTHENTICATION_FAILED " , 401 , " authentication failed " , reason)
56+ return ServerErrorCodeDetail ( code : . authenticationFailed , reason : reason)
5557 case . authenticationRequired( let reason) :
56- return Self . detail ( " AUTHENTICATION_REQUIRED " , 421 , " authentication required " , reason)
58+ return ServerErrorCodeDetail ( code : . authenticationRequired , reason : reason)
5759 case . badRequest( let reason) :
58- return Self . detail ( " BAD_REQUEST " , 400 , " bad request " , reason)
60+ return ServerErrorCodeDetail ( code : . badRequest , reason : reason)
5961 case . conflict( let reason) :
60- return Self . detail ( " CONFLICT " , 409 , " conflict " , reason)
62+ return ServerErrorCodeDetail ( code : . conflict, reason : reason)
6163 case . exists( let reason) :
62- return Self . detail ( " EXISTS " , 409 , " already exists" , reason)
64+ return ServerErrorCodeDetail ( code : . exists, reason : reason)
6365 case . internalServerError( let reason) :
64- return Self . detail ( " INTERNAL_ERROR " , 500 , " internal server error " , reason)
66+ return ServerErrorCodeDetail ( code : . internalError , reason : reason)
6567 case . notFound( let reason) :
66- return Self . detail ( " NOT_FOUND " , 404 , " not found " , reason)
68+ return ServerErrorCodeDetail ( code : . notFound , reason : reason)
6769 case . quotaExceeded( let reason, _) :
68- return Self . detail ( " QUOTA_EXCEEDED " , 413 , " quota exceeded " , reason)
70+ return ServerErrorCodeDetail ( code : . quotaExceeded , reason : reason)
6971 case . throttled( let reason) :
70- return Self . detail ( " THROTTLED " , 429 , " throttled " , reason)
72+ return ServerErrorCodeDetail ( code : . throttled, reason : reason)
7173 case . tryAgainLater( let reason) :
72- return Self . detail ( " TRY_AGAIN_LATER " , 503 , " try again later " , reason)
74+ return ServerErrorCodeDetail ( code : . tryAgainLater , reason : reason)
7375 case . validatingReferenceError( let reason) :
74- return Self . detail ( " VALIDATING_REFERENCE_ERROR " , 412 , " reference validation error " , reason)
76+ return ServerErrorCodeDetail ( code : . validatingReferenceError , reason : reason)
7577 case . zoneNotFound( let reason) :
76- return Self . detail ( " ZONE_NOT_FOUND " , 404 , " zone not found " , reason)
78+ return ServerErrorCodeDetail ( code : . zoneNotFound , reason : reason)
7779 case . unknownServerError( let code, let statusCode, let reason) :
78- return Self . detail ( code, statusCode, " unrecognized server error " , reason)
80+ return ServerErrorCodeDetail (
81+ code: code,
82+ statusCode: statusCode,
83+ summary: ServerErrorCodeDetail . unrecognizedSummary,
84+ reason: reason
85+ )
7986 case . httpError, . httpErrorWithDetails, . httpErrorWithRawResponse, . invalidResponse,
8087 . incompleteResponse, . conversionFailed, . recordOperationFailed,
8188 . subscriptionOperationFailed, . subscriptionLikelyDuplicate, . underlyingError,
@@ -92,7 +99,7 @@ extension CloudKitError {
9299 /// ``CloudKitError/httpErrorWithDetails(statusCode:reason:)``, preserving
93100 /// the server `reason`.
94101 /// - Each of the fourteen codes documented in `openapi.yaml` becomes its own
95- /// dedicated case.
102+ /// dedicated case, looked up via ``CloudKitServerErrorCode``'s dictionary .
96103 /// - Anything else becomes
97104 /// ``CloudKitError/unknownServerError(code:statusCode:reason:)`` so a code
98105 /// Apple adds after this release still reaches the caller intact.
@@ -101,56 +108,62 @@ extension CloudKitError {
101108 /// - code: The raw `serverErrorCode` string from the failure body.
102109 /// - statusCode: The HTTP status the failure arrived with.
103110 /// - reason: The server-supplied `reason`, when present.
104- // swiftlint:disable:next cyclomatic_complexity
105111 internal init ( serverErrorCode code: String ? , statusCode: Int , reason: String ? ) {
106112 guard let code else {
107113 self = . httpErrorWithDetails( statusCode: statusCode, reason: reason)
108114 return
109115 }
110- switch code {
111- case " ACCESS_DENIED " :
112- self = . accessDenied( reason: reason)
113- case " ATOMIC_ERROR " :
114- self = . atomicFailure( reason: reason)
115- case " AUTHENTICATION_FAILED " :
116- self = . authenticationFailed( reason: reason)
117- case " AUTHENTICATION_REQUIRED " :
118- self = . authenticationRequired( reason: reason)
119- case " BAD_REQUEST " :
120- self = . badRequest( reason: reason)
121- case " CONFLICT " :
122- self = . conflict( reason: reason)
123- case " EXISTS " :
124- self = . exists( reason: reason)
125- case " INTERNAL_ERROR " :
126- self = . internalServerError( reason: reason)
127- case " NOT_FOUND " :
128- self = . notFound( reason: reason)
129- case " QUOTA_EXCEEDED " :
130- // `hint` is enriched later by the calling operation's catch block, which
131- // is the only place that can see the local request state.
132- self = . quotaExceeded( reason: reason, hint: nil )
133- case " THROTTLED " :
134- self = . throttled( reason: reason)
135- case " TRY_AGAIN_LATER " :
136- self = . tryAgainLater( reason: reason)
137- case " VALIDATING_REFERENCE_ERROR " :
138- self = . validatingReferenceError( reason: reason)
139- case " ZONE_NOT_FOUND " :
140- self = . zoneNotFound( reason: reason)
141- default :
142- self = . unknownServerError( code: code, statusCode: statusCode, reason: reason)
143- }
116+ // Dictionary lookup in `CloudKitServerErrorCode.init(rawValue:)` — no
117+ // string switch here. Map the typed enum onto the dedicated case.
118+ self = Self . make (
119+ from: CloudKitServerErrorCode ( rawValue: code) ,
120+ statusCode: statusCode,
121+ reason: reason
122+ )
144123 }
145124
146- private static func detail(
147- _ code: String ,
148- _ statusCode: Int ,
149- _ summary: String ,
150- _ reason: String ?
151- ) -> ServerErrorCodeDetail {
152- ServerErrorCodeDetail (
153- code: code, statusCode: statusCode, summary: summary, reason: reason
154- )
125+ /// Builds the dedicated case for a typed ``CloudKitServerErrorCode``.
126+ ///
127+ /// `hint` for ``CloudKitError/quotaExceeded(reason:hint:)`` is enriched later
128+ /// by the calling operation's catch block, which is the only place that can
129+ /// see the local request state.
130+ // swiftlint:disable:next cyclomatic_complexity
131+ private static func make(
132+ from code: CloudKitServerErrorCode ,
133+ statusCode: Int ,
134+ reason: String ?
135+ ) -> CloudKitError {
136+ switch code {
137+ case . accessDenied:
138+ return . accessDenied( reason: reason)
139+ case . atomicError:
140+ return . atomicFailure( reason: reason)
141+ case . authenticationFailed:
142+ return . authenticationFailed( reason: reason)
143+ case . authenticationRequired:
144+ return . authenticationRequired( reason: reason)
145+ case . badRequest:
146+ return . badRequest( reason: reason)
147+ case . conflict:
148+ return . conflict( reason: reason)
149+ case . exists:
150+ return . exists( reason: reason)
151+ case . internalError:
152+ return . internalServerError( reason: reason)
153+ case . notFound:
154+ return . notFound( reason: reason)
155+ case . quotaExceeded:
156+ return . quotaExceeded( reason: reason, hint: nil )
157+ case . throttled:
158+ return . throttled( reason: reason)
159+ case . tryAgainLater:
160+ return . tryAgainLater( reason: reason)
161+ case . validatingReferenceError:
162+ return . validatingReferenceError( reason: reason)
163+ case . zoneNotFound:
164+ return . zoneNotFound( reason: reason)
165+ case . unknown( let raw) :
166+ return . unknownServerError( code: raw, statusCode: statusCode, reason: reason)
167+ }
155168 }
156169}
0 commit comments