Skip to content

Commit f8cbe4f

Browse files
darinfcompnerd
authored andcommitted
Cleanup error code types (#62)
Goals: - Allow consumers to cast `Error` instances received to `AuthErrorCode` or `FirestoreErrorCode`, corresponding to the API used. - Allow consumers to write code like `AuthErrorCode.userDisabled`. - Allow consumers to write code like `AuthErrorCode(.userDisabled)`. - Make sure `{Auth,Firestore}ErrorCode` conform to `RawRepresentable` with a `RawValue` of type `Int`. Also changes: - Removes `FirebaseError` now that it is no longer needed. This required changing `resultAndError` to be generic on the error type returned. This is necessary since the underlying C++ API does not have any type information for the error codes produced (they are just integers). At the Swift layer we want to generate the right concrete error types. This is all intended for better conformance to how these types are reflected to Swift from Obj C. Intentionally not trying to make these error types extend from `NSError` at this time. That may be interesting follow-up work.
1 parent 0470595 commit f8cbe4f

File tree

10 files changed

+214
-159
lines changed

10 files changed

+214
-159
lines changed

CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ endif()
5151
add_library(FirebaseCore SHARED
5252
Sources/FirebaseCore/FirebaseApp+Swift.swift
5353
Sources/FirebaseCore/FirebaseConfiguration.swift
54-
Sources/FirebaseCore/FirebaseError.swift
5554
Sources/FirebaseCore/FirebaseLogging+Swift.swift
5655
Sources/FirebaseCore/FirebaseOptions+Swift.swift
5756
Sources/FirebaseCore/FutureProtocol.swift)

Sources/FirebaseAuth/FirebaseAuth+Swift.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public final class Auth {
102102
private func fetchSignInMethodsImpl(forEmail email: String, completion: @escaping ([String]?, Error?) -> Void) {
103103
let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_fetch_providers_for_email(impl, email)
104104
future.setCompletion({
105-
let (result, error) = future.resultAndError
105+
let (result, error) = future.resultAndError { AuthErrorCode($0) }
106106
var providers: [String]?
107107
if let result {
108108
providers = result.providers.map(String.init)
@@ -139,7 +139,7 @@ public final class Auth {
139139
private func signInImpl(withEmail email: String, password: String, completion: @escaping (AuthDataResult?, Error?) -> Void) {
140140
let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_sign_in_with_email_and_password(impl, email, password)
141141
future.setCompletion({
142-
let (result, error) = future.resultAndError
142+
let (result, error) = future.resultAndError { AuthErrorCode($0) }
143143
var data: AuthDataResult?
144144
if let result {
145145
data = .init(result)
@@ -197,7 +197,7 @@ public final class Auth {
197197
private func createUserImpl(withEmail email: String, password: String, completion: @escaping (AuthDataResult?, Error?) -> Void) {
198198
let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_create_user_with_email_and_password(impl, email, password)
199199
future.setCompletion({
200-
let (result, error) = future.resultAndError
200+
let (result, error) = future.resultAndError { AuthErrorCode($0) }
201201
var data: AuthDataResult?
202202
if let result {
203203
data = .init(result)
@@ -250,7 +250,7 @@ public final class Auth {
250250
private func sendPasswordResetImpl(withEmail email: String, completion: @escaping (Error?) -> Void) {
251251
let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_send_password_reset_email(impl, email)
252252
future.setCompletion({
253-
let (_, error) = future.resultAndError
253+
let (_, error) = future.resultAndError { AuthErrorCode($0) }
254254
completion(error)
255255
})
256256
}
Lines changed: 135 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,153 @@
11
// SPDX-License-Identifier: BSD-3-Clause
22

3-
import Foundation
3+
@_exported
4+
import firebase
5+
@_spi(FirebaseInternal)
6+
import FirebaseCore
47

5-
public enum AuthErrorCode: Int {
6-
case invalidCustomToken = 2
7-
case customTokenMismatch = 3
8-
case invalidCredential = 4
9-
case userDisabled = 5
10-
case accountExistsWithDifferentCredential = 6
11-
case operationNotAllowed = 7
12-
case emailAlreadyInUse = 8
13-
case requiresRecentLogin = 9
14-
case credentialAlreadyInUse = 10
15-
case invalidEmail = 11
16-
case wrongPassword = 12
17-
case tooManyRequests = 13
18-
case userNotFound = 14
19-
case providerAlreadyLinked = 15
20-
case noSuchProvider = 16
21-
case invalidUserToken = 17
22-
case userTokenExpired = 18
23-
case networkError = 19
24-
case invalidAPIKey = 20
25-
case appNotAuthorized = 21
26-
case userMismatch = 22
27-
case weakPassword = 23
28-
case noSignedInUser = 24
29-
case apiNotAvailable = 25
30-
case expiredActionCode = 26
31-
case invalidActionCode = 27
32-
case invalidMessagePayload = 28
33-
case invalidPhoneNumber = 29
34-
case missingPhoneNumber = 30
35-
case invalidRecipientEmail = 31
36-
case invalidSender = 32
37-
case invalidVerificationCode = 33
38-
case invalidVerificationID = 34
39-
case missingVerificationCode = 35
40-
case missingVerificationID = 36
41-
case missingEmail = 37
42-
case missingPassword = 38
43-
case quotaExceeded = 39
44-
case retryPhoneAuth = 40
45-
case sessionExpired = 41
46-
case appNotVerified = 42
47-
case appVerificationUserInteractionFailure = 43
48-
case captchaCheckFailed = 44
49-
case invalidAppCredential = 45
50-
case missingAppCredential = 46
51-
case invalidClientID = 47
52-
case invalidContinueURI = 48
53-
case missingContinueURI = 49
54-
case keychainError = 50
55-
case missingAppToken = 51
56-
case missingIosBundleID = 52
57-
case notificationNotForwarded = 53
58-
case unauthorizedDomain = 54
59-
case webContextAlreadyPresented = 55
60-
case webContextCancelled = 56
61-
case dynamicLinkNotActivated = 57
62-
case cancelled = 58
63-
case invalidProviderID = 59
64-
case webInternalError = 60
8+
public struct AuthErrorCode: Error {
9+
public let rawValue: Int
10+
public let localizedDescription: String
11+
12+
internal init(_ params: (code: Int32, message: String)) {
13+
self.rawValue = Int(params.code)
14+
localizedDescription = params.message
15+
}
16+
17+
private init(_ error: firebase.auth.AuthError) {
18+
self.init(rawValue: Int(error.rawValue))
19+
}
20+
}
21+
22+
extension AuthErrorCode: RawRepresentable {
23+
public typealias RawValue = Int
24+
25+
public init(rawValue: Int) {
26+
self.rawValue = rawValue
27+
localizedDescription = "\(rawValue)"
28+
}
29+
}
30+
31+
extension AuthErrorCode {
32+
public static var none: Self { .init(firebase.auth.kAuthErrorNone) }
33+
public static var unimplemented: Self { .init(firebase.auth.kAuthErrorUnimplemented) }
34+
public static var invalidCustomToken: Self { .init(firebase.auth.kAuthErrorInvalidCustomToken) }
35+
public static var customTokenMismatch: Self { .init(firebase.auth.kAuthErrorCustomTokenMismatch) }
36+
public static var invalidCredential: Self { .init(firebase.auth.kAuthErrorInvalidCredential) }
37+
public static var userDisabled: Self { .init(firebase.auth.kAuthErrorUserDisabled) }
38+
public static var accountExistsWithDifferentCredential: Self { .init(firebase.auth.kAuthErrorAccountExistsWithDifferentCredentials) }
39+
public static var operationNotAllowed: Self { .init(firebase.auth.kAuthErrorOperationNotAllowed) }
40+
public static var emailAlreadyInUse: Self { .init(firebase.auth.kAuthErrorEmailAlreadyInUse) }
41+
public static var requiresRecentLogin: Self { .init(firebase.auth.kAuthErrorRequiresRecentLogin) }
42+
public static var credentialAlreadyInUse: Self { .init(firebase.auth.kAuthErrorCredentialAlreadyInUse) }
43+
public static var invalidEmail: Self { .init(firebase.auth.kAuthErrorInvalidEmail) }
44+
public static var wrongPassword: Self { .init(firebase.auth.kAuthErrorWrongPassword) }
45+
public static var tooManyRequests: Self { .init(firebase.auth.kAuthErrorTooManyRequests) }
46+
public static var userNotFound: Self { .init(firebase.auth.kAuthErrorUserNotFound) }
47+
public static var providerAlreadyLinked: Self { .init(firebase.auth.kAuthErrorProviderAlreadyLinked) }
48+
public static var noSuchProvider: Self { .init(firebase.auth.kAuthErrorNoSuchProvider) }
49+
public static var invalidUserToken: Self { .init(firebase.auth.kAuthErrorInvalidUserToken) }
50+
public static var userTokenExpired: Self { .init(firebase.auth.kAuthErrorUserTokenExpired) }
51+
public static var networkError: Self { .init(firebase.auth.kAuthErrorNetworkRequestFailed) }
52+
public static var invalidAPIKey: Self { .init(firebase.auth.kAuthErrorInvalidApiKey) }
53+
public static var appNotAuthorized: Self { .init(firebase.auth.kAuthErrorAppNotAuthorized) }
54+
public static var userMismatch: Self { .init(firebase.auth.kAuthErrorUserMismatch) }
55+
public static var weakPassword: Self { .init(firebase.auth.kAuthErrorWeakPassword) }
56+
public static var noSignedInUser: Self { .init(firebase.auth.kAuthErrorNoSignedInUser) }
57+
public static var apiNotAvailable: Self { .init(firebase.auth.kAuthErrorApiNotAvailable) }
58+
public static var expiredActionCode: Self { .init(firebase.auth.kAuthErrorExpiredActionCode) }
59+
public static var invalidActionCode: Self { .init(firebase.auth.kAuthErrorInvalidActionCode) }
60+
public static var invalidMessagePayload: Self { .init(firebase.auth.kAuthErrorInvalidMessagePayload) }
61+
public static var invalidPhoneNumber: Self { .init(firebase.auth.kAuthErrorInvalidPhoneNumber) }
62+
public static var missingPhoneNumber: Self { .init(firebase.auth.kAuthErrorMissingPhoneNumber) }
63+
public static var invalidRecipientEmail: Self { .init(firebase.auth.kAuthErrorInvalidRecipientEmail) }
64+
public static var invalidSender: Self { .init(firebase.auth.kAuthErrorInvalidSender) }
65+
public static var invalidVerificationCode: Self { .init(firebase.auth.kAuthErrorInvalidVerificationCode) }
66+
public static var invalidVerificationID: Self { .init(firebase.auth.kAuthErrorInvalidVerificationId) }
67+
public static var missingVerificationCode: Self { .init(firebase.auth.kAuthErrorMissingVerificationCode) }
68+
public static var missingVerificationID: Self { .init(firebase.auth.kAuthErrorMissingVerificationId) }
69+
public static var missingEmail: Self { .init(firebase.auth.kAuthErrorMissingEmail) }
70+
public static var missingPassword: Self { .init(firebase.auth.kAuthErrorMissingPassword) }
71+
public static var quotaExceeded: Self { .init(firebase.auth.kAuthErrorQuotaExceeded) }
72+
public static var retryPhoneAuth: Self { .init(firebase.auth.kAuthErrorRetryPhoneAuth) }
73+
public static var sessionExpired: Self { .init(firebase.auth.kAuthErrorSessionExpired) }
74+
public static var appNotVerified: Self { .init(firebase.auth.kAuthErrorAppNotVerified) }
75+
public static var appVerificationUserInteractionFailure: Self { .init(firebase.auth.kAuthErrorAppVerificationFailed) }
76+
public static var captchaCheckFailed: Self { .init(firebase.auth.kAuthErrorCaptchaCheckFailed) }
77+
public static var invalidAppCredential: Self { .init(firebase.auth.kAuthErrorInvalidAppCredential) }
78+
public static var missingAppCredential: Self { .init(firebase.auth.kAuthErrorMissingAppCredential) }
79+
public static var invalidClientID: Self { .init(firebase.auth.kAuthErrorInvalidClientId) }
80+
public static var invalidContinueURI: Self { .init(firebase.auth.kAuthErrorInvalidContinueUri) }
81+
public static var missingContinueURI: Self { .init(firebase.auth.kAuthErrorMissingContinueUri) }
82+
public static var keychainError: Self { .init(firebase.auth.kAuthErrorKeychainError) }
83+
public static var missingAppToken: Self { .init(firebase.auth.kAuthErrorMissingAppToken) }
84+
public static var missingIosBundleID: Self { .init(firebase.auth.kAuthErrorMissingIosBundleId) }
85+
public static var notificationNotForwarded: Self { .init(firebase.auth.kAuthErrorNotificationNotForwarded) }
86+
public static var unauthorizedDomain: Self { .init(firebase.auth.kAuthErrorUnauthorizedDomain) }
87+
public static var webContextAlreadyPresented: Self { .init(firebase.auth.kAuthErrorWebContextAlreadyPresented) }
88+
public static var webContextCancelled: Self { .init(firebase.auth.kAuthErrorWebContextCancelled) }
89+
public static var dynamicLinkNotActivated: Self { .init(firebase.auth.kAuthErrorDynamicLinkNotActivated) }
90+
public static var cancelled: Self { .init(firebase.auth.kAuthErrorCancelled) }
91+
public static var invalidProviderID: Self { .init(firebase.auth.kAuthErrorInvalidProviderId) }
92+
public static var webInternalError: Self { .init(firebase.auth.kAuthErrorWebInternalError) }
6593
// There's a typo in the Firebase error, carrying it over here.
66-
case webStorateUnsupported = 61
67-
case tenantIDMismatch = 62
68-
case unsupportedTenantOperation = 63
69-
case invalidDynamicLinkDomain = 64
70-
case rejectedCredential = 65
71-
case phoneNumberNotFound = 66
72-
case invalidTenantID = 67
73-
case missingClientIdentifier = 68
74-
case missingMultiFactorSession = 69
75-
case missingMultiFactorInfo = 70
76-
case invalidMultiFactorSession = 71
77-
case multiFactorInfoNotFound = 72
78-
case adminRestrictedOperation = 73
79-
case unverifiedEmail = 74
80-
case secondFactorAlreadyEnrolled = 75
81-
case maximumSecondFactorCountExceeded = 76
82-
case unsupportedFirstFactor = 77
83-
case emailChangeNeedsVerification = 78
94+
public static var webStorateUnsupported: Self { .init(firebase.auth.kAuthErrorWebStorateUnsupported) }
95+
public static var tenantIDMismatch: Self { .init(firebase.auth.kAuthErrorTenantIdMismatch) }
96+
public static var unsupportedTenantOperation: Self { .init(firebase.auth.kAuthErrorUnsupportedTenantOperation) }
97+
public static var invalidDynamicLinkDomain: Self { .init(firebase.auth.kAuthErrorInvalidLinkDomain) }
98+
public static var rejectedCredential: Self { .init(firebase.auth.kAuthErrorRejectedCredential) }
99+
public static var phoneNumberNotFound: Self { .init(firebase.auth.kAuthErrorPhoneNumberNotFound) }
100+
public static var invalidTenantID: Self { .init(firebase.auth.kAuthErrorInvalidTenantId) }
101+
public static var missingClientIdentifier: Self { .init(firebase.auth.kAuthErrorMissingClientIdentifier) }
102+
public static var missingMultiFactorSession: Self { .init(firebase.auth.kAuthErrorMissingMultiFactorSession) }
103+
public static var missingMultiFactorInfo: Self { .init(firebase.auth.kAuthErrorMissingMultiFactorInfo) }
104+
public static var invalidMultiFactorSession: Self { .init(firebase.auth.kAuthErrorInvalidMultiFactorSession) }
105+
public static var multiFactorInfoNotFound: Self { .init(firebase.auth.kAuthErrorMultiFactorInfoNotFound) }
106+
public static var adminRestrictedOperation: Self { .init(firebase.auth.kAuthErrorAdminRestrictedOperation) }
107+
public static var unverifiedEmail: Self { .init(firebase.auth.kAuthErrorUnverifiedEmail) }
108+
public static var secondFactorAlreadyEnrolled: Self { .init(firebase.auth.kAuthErrorSecondFactorAlreadyEnrolled) }
109+
public static var maximumSecondFactorCountExceeded: Self { .init(firebase.auth.kAuthErrorMaximumSecondFactorCountExceeded) }
110+
public static var unsupportedFirstFactor: Self { .init(firebase.auth.kAuthErrorUnsupportedFirstFactor) }
111+
public static var emailChangeNeedsVerification: Self { .init(firebase.auth.kAuthErrorEmailChangeNeedsVerification) }
84112
#if INTERNAL_EXPERIMENTAL
85-
case invalidEventHandler = 79
86-
case federatedProviderAlreadyInUse = 80
87-
case invalidAuthenticatedUserData = 81
88-
case federatedSignInUserInteractionFailure = 82
89-
case missingOrInvalidNonce = 83
90-
case userCancelled = 84
91-
case unsupportedPassthroughOperation = 85
92-
case tokenRefreshUnavailable = 86
113+
public static var invalidEventHandler: Self { .init(firebase.auth.kAuthErrorInvalidEventHandler) }
114+
public static var federatedProviderAlreadyInUse: Self { .init(firebase.auth.kAuthErrorFederatedProviderAlreadyInUse) }
115+
public static var invalidAuthenticatedUserData: Self { .init(firebase.auth.kAuthErrorInvalidAuthenticatedUserData) }
116+
public static var federatedSignInUserInteractionFailure: Self { .init(firebase.auth.kAuthErrorFederatedSignInUserInteractionFailure) }
117+
public static var missingOrInvalidNonce: Self { .init(firebase.auth.kAuthErrorMissingOrInvalidNonce) }
118+
public static var userCancelled: Self { .init(firebase.auth.kAuthErrorUserCancelled) }
119+
public static var unsupportedPassthroughOperation: Self { .init(firebase.auth.kAuthErrorUnsupportedPassthroughOperation) }
120+
public static var tokenRefreshUnavailable: Self { .init(firebase.auth.kAuthErrorTokenRefreshUnavailable) }
93121
#endif
94122

95123
// Errors that are not represented in the C++ SDK, but are
96124
// present in the reference API.
97-
case missingAndroidPackageName = 17037
98-
case webNetworkRequestFailed = 17061
99-
case webSignInUserInteractionFailure = 17063
100-
case localPlayerNotAuthenticated = 17066
101-
case nullUser = 17067
102-
case gameKitNotLinked = 17076
103-
case secondFactorRequired = 17078
104-
case blockingCloudFunctionError = 17105
105-
case internalError = 17999
106-
case malformedJWT = 18000
125+
public static var missingAndroidPackageName: Self { .init(rawValue: 17037) }
126+
public static var webNetworkRequestFailed: Self { .init(rawValue: 17061) }
127+
public static var webSignInUserInteractionFailure: Self { .init(rawValue: 17063) }
128+
public static var localPlayerNotAuthenticated: Self { .init(rawValue: 17066) }
129+
public static var nullUser: Self { .init(rawValue: 17067) }
130+
public static var gameKitNotLinked: Self { .init(rawValue: 17076) }
131+
public static var secondFactorRequired: Self { .init(rawValue: 17078) }
132+
public static var blockingCloudFunctionError: Self { .init(rawValue: 17105) }
133+
public static var internalError: Self { .init(rawValue: 17999) }
134+
public static var malformedJWT: Self { .init(rawValue: 18000) }
107135
}
108136

109-
extension AuthErrorCode: Error {}
137+
extension AuthErrorCode: Equatable {}
138+
110139
extension AuthErrorCode {
140+
// The Obj C API provides this type as well, so provide it here for consistency.
141+
public typealias Code = AuthErrorCode
142+
111143
// This allows us to re-expose self as a code similarly
112144
// to what the Firebase SDK does when it creates the
113145
// underlying NSErrors on iOS/macOS.
114-
public var code: AuthErrorCode {
146+
public var code: Code {
115147
return self
116148
}
149+
150+
public init(_ code: Code) {
151+
self.init(rawValue: code.rawValue)
152+
}
117153
}

Sources/FirebaseAuth/FirebaseUser+Swift.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public final class User {
9898
private func reloadImpl(completion: @escaping (Error?) -> Void) {
9999
let future = swift_firebase.swift_cxx_shims.firebase.auth.user_reload(impl)
100100
future.setCompletion({
101-
let (_, error) = future.resultAndError
101+
let (_, error) = future.resultAndError { AuthErrorCode($0) }
102102
completion(error)
103103
})
104104
}
@@ -128,7 +128,7 @@ public final class User {
128128
public func reauthenticateImpl(with credential: Credential, completion: @escaping (AuthResult?, Error?) -> Void) {
129129
let future = swift_firebase.swift_cxx_shims.firebase.auth.user_reauthenticate_and_retrieve_data(impl, credential)
130130
future.setCompletion({
131-
let (result, error) = future.resultAndError
131+
let (result, error) = future.resultAndError { AuthErrorCode($0) }
132132
completion(result, error)
133133
})
134134
}
@@ -178,7 +178,7 @@ public final class User {
178178
private func idTokenForcingRefreshImpl(_ forceRefresh: Bool, completion: @escaping (String?, Error?) -> Void) {
179179
let future = swift_firebase.swift_cxx_shims.firebase.auth.user_get_token(impl, forceRefresh)
180180
future.setCompletion({
181-
let (result, error) = future.resultAndError
181+
let (result, error) = future.resultAndError { AuthErrorCode($0) }
182182
let stringResult: String?
183183
if let result {
184184
stringResult = String(result)
@@ -225,7 +225,7 @@ public final class User {
225225
public func sendEmailVerificationImpl(completion: @escaping (Error?) -> Void) {
226226
let future = swift_firebase.swift_cxx_shims.firebase.auth.user_send_email_verification(impl)
227227
future.setCompletion({
228-
let (_, error) = future.resultAndError
228+
let (_, error) = future.resultAndError { AuthErrorCode($0) }
229229
completion(error)
230230
})
231231
}

Sources/FirebaseCore/FirebaseError.swift

Lines changed: 0 additions & 12 deletions
This file was deleted.

Sources/FirebaseCore/FutureProtocol.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ public extension FutureProtocol {
4747
return String(cString: errorMessageUnsafe)
4848
}
4949

50-
var resultAndError: (ResultType?, Error?) {
51-
let error = error()
50+
func resultAndError<ErrorType>(_ constructError: ((Int32, String)) -> ErrorType) -> (ResultType?, ErrorType?) {
51+
let error = self.error()
5252
guard error == 0 else {
53-
return (nil, FirebaseError(code: error, message: errorMessage!))
53+
return (nil, constructError((code: error, message: errorMessage!)))
5454
}
5555
return (result, nil)
5656
}

0 commit comments

Comments
 (0)