Skip to content

Commit

Permalink
Merge branch 'master' into auth-result
Browse files Browse the repository at this point in the history
  • Loading branch information
nbransby authored Aug 30, 2024
2 parents 3eb1a0c + ba73861 commit ecfa47d
Show file tree
Hide file tree
Showing 14 changed files with 103 additions and 92 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,4 +329,3 @@ More recently, with the official SDK for Android providing better support for Ko
For contributors this means following these points when adding new code to the public API of this project:
- **Match the [Android SDKs API](https://firebase.google.com/docs/reference/kotlin/packages).** When adding new API coverage use the Android SDK as the guide on what the public API should be in regard to naming, parameters etc. The goal here is *near binary compatibility*, meaning code consuming the Android SDK compiles *as is* with the Kotlin SDK after just changing the package imports from `com.google` to `dev.gitlive`.
- **Follow our [Kotlin-first design](https://github.com/GitLiveApp/firebase-kotlin-sdk/?tab=readme-ov-file#kotlin-first-design) principles when needed.** If the API you are adding coverage for is new, and it's Kotlin-first in the Android SDK, then you can simply just match the Android SDKs API as described in the first point, but if it's an older Java-first API then ideally we would include an identical API for API compatibility *plus* a Kotlin-first overload. A good example for this is where the Builder pattern is employed in the Android SDK, here we can follow [this Kotlin-first design principle](https://github.com/GitLiveApp/firebase-kotlin-sdk/?tab=readme-ov-file#default-arguments) and provide both methods, one taking the options created with the builder and an overload with default arguments to avoid the builder boilerplate for developers not porting an existing android code base.

2 changes: 1 addition & 1 deletion firebase-auth/api/android/firebase-auth.api
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public final class dev/gitlive/firebase/auth/AuthTokenResult {
}

public final class dev/gitlive/firebase/auth/CredentialsKt {
public static final fun getAndroid (Lcom/google/firebase/auth/OAuthProvider;)Lcom/google/firebase/auth/OAuthProvider;
public static final fun getAndroid (Ldev/gitlive/firebase/auth/OAuthProvider;)Lcom/google/firebase/auth/OAuthProvider;
}

public final class dev/gitlive/firebase/auth/EmailAuthProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package dev.gitlive.firebase.auth

import android.app.Activity
import com.google.firebase.FirebaseException
import com.google.firebase.auth.OAuthProvider
import com.google.firebase.auth.OAuthProvider as AndroidOAuthProvider
import com.google.firebase.auth.PhoneAuthOptions
import com.google.firebase.auth.PhoneAuthProvider
import kotlinx.coroutines.CompletableDeferred
Expand Down Expand Up @@ -52,17 +52,17 @@ public actual object GoogleAuthProvider {
}
}

public val OAuthProvider.android: OAuthProvider get() = android
public val OAuthProvider.android: AndroidOAuthProvider get() = android

public actual class OAuthProvider(internal val android: OAuthProvider) {
public actual class OAuthProvider(internal val android: AndroidOAuthProvider) {

public actual constructor(
provider: String,
scopes: List<String>,
customParameters: Map<String, String>,
auth: FirebaseAuth,
) : this(
OAuthProvider
AndroidOAuthProvider
.newBuilder(provider, auth.android)
.setScopes(scopes)
.addCustomParameters(customParameters)
Expand All @@ -71,7 +71,7 @@ public actual class OAuthProvider(internal val android: OAuthProvider) {

public actual companion object {
public actual fun credential(providerId: String, accessToken: String?, idToken: String?, rawNonce: String?): OAuthCredential {
val builder = OAuthProvider.newCredentialBuilder(providerId)
val builder = AndroidOAuthProvider.newCredentialBuilder(providerId)
accessToken?.let { builder.setAccessToken(it) }
idToken?.let { builder.setIdToken(it) }
rawNonce?.let { builder.setIdTokenWithRawNonce(idToken!!, it) }
Expand Down
96 changes: 52 additions & 44 deletions firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public actual fun Firebase.auth(app: FirebaseApp): FirebaseAuth = FirebaseAuth(
public actual class FirebaseAuth internal constructor(internal val ios: FIRAuth) {

public actual val currentUser: FirebaseUser?
get() = ios.currentUser?.let { FirebaseUser(it) }
get() = ios.currentUser()?.let { FirebaseUser(it) }

public actual val authStateChanged: Flow<FirebaseUser?> get() = callbackFlow {
val handle = ios.addAuthStateDidChangeListener { _, user -> trySend(user?.let { FirebaseUser(it) }) }
Expand All @@ -45,7 +45,7 @@ public actual class FirebaseAuth internal constructor(internal val ios: FIRAuth)
}

public actual var languageCode: String
get() = ios.languageCode ?: ""
get() = ios.languageCode() ?: ""
set(value) {
ios.setLanguageCode(value)
}
Expand Down Expand Up @@ -91,15 +91,15 @@ public actual class FirebaseAuth internal constructor(internal val ios: FIRAuth)
public actual suspend fun <T : ActionCodeResult> checkActionCode(code: String): T {
val result: FIRActionCodeInfo = ios.awaitResult { checkActionCode(code, it) }
@Suppress("UNCHECKED_CAST")
return when (result.operation) {
return when (result.operation()) {
FIRActionCodeOperationEmailLink -> SignInWithEmailLink
FIRActionCodeOperationVerifyEmail -> VerifyEmail(result.email!!)
FIRActionCodeOperationPasswordReset -> PasswordReset(result.email!!)
FIRActionCodeOperationRecoverEmail -> RecoverEmail(result.email!!, result.previousEmail!!)
FIRActionCodeOperationVerifyAndChangeEmail -> VerifyBeforeChangeEmail(result.email!!, result.previousEmail!!)
FIRActionCodeOperationRevertSecondFactorAddition -> RevertSecondFactorAddition(result.email!!, null)
FIRActionCodeOperationUnknown -> throw UnsupportedOperationException(result.operation.toString())
else -> throw UnsupportedOperationException(result.operation.toString())
FIRActionCodeOperationVerifyEmail -> VerifyEmail(result.email())
FIRActionCodeOperationPasswordReset -> PasswordReset(result.email())
FIRActionCodeOperationRecoverEmail -> RecoverEmail(result.email(), result.previousEmail()!!)
FIRActionCodeOperationVerifyAndChangeEmail -> VerifyBeforeChangeEmail(result.email(), result.previousEmail()!!)
FIRActionCodeOperationRevertSecondFactorAddition -> RevertSecondFactorAddition(result.email(), null)
FIRActionCodeOperationUnknown -> throw UnsupportedOperationException(result.operation().toString())
else -> throw UnsupportedOperationException(result.operation().toString())
} as T
}

Expand All @@ -110,7 +110,7 @@ public val AuthResult.ios: FIRAuthDataResult get() = ios

public actual class AuthResult(internal val ios: FIRAuthDataResult) {
public actual val user: FirebaseUser?
get() = FirebaseUser(ios.user)
get() = FirebaseUser(ios.user())
public actual val credential: AuthCredential?
get() = ios.credential?.let { AuthCredential(it) }
public actual val additionalUserInfo: AdditionalUserInfo?
Expand Down Expand Up @@ -145,23 +145,23 @@ public actual class AuthTokenResult(internal val ios: FIRAuthTokenResult) {
// actual val authTimestamp: Long
// get() = ios.authDate
public actual val claims: Map<String, Any>
get() = ios.claims.map { it.key.toString() to it.value as Any }.toMap()
get() = ios.claims().map { it.key.toString() to it.value as Any }.toMap()

// actual val expirationTimestamp: Long
// get() = ios.expirationDate
// actual val issuedAtTimestamp: Long
// get() = ios.issuedAtDate
public actual val signInProvider: String?
get() = ios.signInProvider
get() = ios.signInProvider()
public actual val token: String?
get() = ios.token
get() = ios.token()
}

internal fun ActionCodeSettings.toIos() = FIRActionCodeSettings().also {
it.URL = NSURL.URLWithString(url)
it.setURL(NSURL.URLWithString(url))
androidPackageName?.run { it.setAndroidPackageName(packageName, installIfNotAvailable, minimumVersion) }
it.dynamicLinkDomain = dynamicLinkDomain
it.handleCodeInApp = canHandleCodeInApp
it.setDynamicLinkDomain(dynamicLinkDomain)
it.setHandleCodeInApp(canHandleCodeInApp)
iOSBundleId?.run { it.setIOSBundleID(this) }
}

Expand Down Expand Up @@ -213,47 +213,55 @@ internal suspend inline fun <T> T.await(function: T.(callback: (NSError?) -> Uni
}

private fun NSError.toException() = when (domain) {
// codes from AuthErrors.swift: https://github.com/firebase/firebase-ios-sdk/blob/
// 2f6ac4c2c61cd57c7ea727009e187b7e1163d613/FirebaseAuth/Sources/Swift/Utilities/
// AuthErrors.swift#L51
FIRAuthErrorDomain -> when (code) {
FIRAuthErrorCodeInvalidActionCode,
FIRAuthErrorCodeExpiredActionCode,
17030L, // AuthErrorCode.invalidActionCode
17029L, // AuthErrorCode.expiredActionCode
-> FirebaseAuthActionCodeException(toString())

FIRAuthErrorCodeInvalidEmail -> FirebaseAuthEmailException(toString())

FIRAuthErrorCodeCaptchaCheckFailed,
FIRAuthErrorCodeInvalidPhoneNumber,
FIRAuthErrorCodeMissingPhoneNumber,
FIRAuthErrorCodeInvalidVerificationID,
FIRAuthErrorCodeInvalidVerificationCode,
FIRAuthErrorCodeMissingVerificationID,
FIRAuthErrorCodeMissingVerificationCode,
FIRAuthErrorCodeUserTokenExpired,
FIRAuthErrorCodeInvalidCredential,
17008L, // AuthErrorCode.invalidEmail
-> FirebaseAuthEmailException(toString())

17056L, // AuthErrorCode.captchaCheckFailed
17042L, // AuthErrorCode.invalidPhoneNumber
17041L, // AuthErrorCode.missingPhoneNumber
17046L, // AuthErrorCode.invalidVerificationID
17044L, // AuthErrorCode.invalidVerificationCode
17045L, // AuthErrorCode.missingVerificationID
17043L, // AuthErrorCode.missingVerificationCode
17021L, // AuthErrorCode.userTokenExpired
17004L, // AuthErrorCode.invalidCredential
-> FirebaseAuthInvalidCredentialsException(toString())

FIRAuthErrorCodeWeakPassword -> FirebaseAuthWeakPasswordException(toString())
17026L, // AuthErrorCode.weakPassword
-> FirebaseAuthWeakPasswordException(toString())

FIRAuthErrorCodeInvalidUserToken -> FirebaseAuthInvalidUserException(toString())
17017L, // AuthErrorCode.invalidUserToken
-> FirebaseAuthInvalidUserException(toString())

FIRAuthErrorCodeRequiresRecentLogin -> FirebaseAuthRecentLoginRequiredException(toString())
17014L, // AuthErrorCode.requiresRecentLogin
-> FirebaseAuthRecentLoginRequiredException(toString())

FIRAuthErrorCodeSecondFactorAlreadyEnrolled,
FIRAuthErrorCodeSecondFactorRequired,
FIRAuthErrorCodeMaximumSecondFactorCountExceeded,
FIRAuthErrorCodeMultiFactorInfoNotFound,
17087L, // AuthErrorCode.secondFactorAlreadyEnrolled
17078L, // AuthErrorCode.secondFactorRequired
17088L, // AuthErrorCode.maximumSecondFactorCountExceeded
17084L, // AuthErrorCode.multiFactorInfoNotFound
-> FirebaseAuthMultiFactorException(toString())

FIRAuthErrorCodeEmailAlreadyInUse,
FIRAuthErrorCodeAccountExistsWithDifferentCredential,
FIRAuthErrorCodeCredentialAlreadyInUse,
17007L, // AuthErrorCode.emailAlreadyInUse
17012L, // AuthErrorCode.accountExistsWithDifferentCredential
17025L, // AuthErrorCode.credentialAlreadyInUse
-> FirebaseAuthUserCollisionException(toString())

FIRAuthErrorCodeWebContextAlreadyPresented,
FIRAuthErrorCodeWebContextCancelled,
FIRAuthErrorCodeWebInternalError,
17057L, // AuthErrorCode.webContextAlreadyPresented
17058L, // AuthErrorCode.webContextCancelled
17062L, // AuthErrorCode.webInternalError
-> FirebaseAuthWebException(toString())

FIRAuthErrorCodeNetworkError -> FirebaseNetworkException(toString())
17020L, // AuthErrorCode.networkError
-> FirebaseNetworkException(toString())

else -> FirebaseAuthException(toString())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import cocoapods.FirebaseAuth.*

public actual open class AuthCredential(public open val ios: FIRAuthCredential) {
public actual val providerId: String
get() = ios.provider
get() = ios.provider()
}

public actual class PhoneAuthCredential(override val ios: FIRPhoneAuthCredential) : AuthCredential(ios)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public val MultiFactor.ios: FIRMultiFactor get() = ios

public actual class MultiFactor(internal val ios: FIRMultiFactor) {
public actual val enrolledFactors: List<MultiFactorInfo>
get() = ios.enrolledFactors.mapNotNull { info -> (info as? FIRMultiFactorInfo)?.let { MultiFactorInfo(it) } }
get() = ios.enrolledFactors().mapNotNull { info -> (info as? FIRMultiFactorInfo)?.let { MultiFactorInfo(it) } }
public actual suspend fun enroll(multiFactorAssertion: MultiFactorAssertion, displayName: String?): Unit = ios.await { enrollWithAssertion(multiFactorAssertion.ios, displayName, it) }
public actual suspend fun getSession(): MultiFactorSession = MultiFactorSession(ios.awaitResult { getSessionWithCompletion(completion = it) })
public actual suspend fun unenroll(multiFactorInfo: MultiFactorInfo): Unit = ios.await { unenrollWithInfo(multiFactorInfo.ios, it) }
Expand All @@ -21,20 +21,20 @@ public val MultiFactorInfo.ios: FIRMultiFactorInfo get() = ios

public actual class MultiFactorInfo(internal val ios: FIRMultiFactorInfo) {
public actual val displayName: String?
get() = ios.displayName
get() = ios.displayName()
public actual val enrollmentTime: Double
get() = ios.enrollmentDate.timeIntervalSinceReferenceDate
get() = ios.enrollmentDate().timeIntervalSinceReferenceDate
public actual val factorId: String
get() = ios.factorID
get() = ios.factorID()
public actual val uid: String
get() = ios.UID
get() = ios.UID()
}

public val MultiFactorAssertion.ios: FIRMultiFactorAssertion get() = ios

public actual class MultiFactorAssertion(internal val ios: FIRMultiFactorAssertion) {
public actual val factorId: String
get() = ios.factorID
get() = ios.factorID()
}

public val MultiFactorSession.ios: FIRMultiFactorSession get() = ios
Expand All @@ -44,9 +44,9 @@ public actual class MultiFactorSession(internal val ios: FIRMultiFactorSession)
public val MultiFactorResolver.ios: FIRMultiFactorResolver get() = ios

public actual class MultiFactorResolver(internal val ios: FIRMultiFactorResolver) {
public actual val auth: FirebaseAuth = FirebaseAuth(ios.auth)
public actual val hints: List<MultiFactorInfo> = ios.hints.mapNotNull { hint -> (hint as? FIRMultiFactorInfo)?.let { MultiFactorInfo(it) } }
public actual val session: MultiFactorSession = MultiFactorSession(ios.session)
public actual val auth: FirebaseAuth = FirebaseAuth(ios.auth())
public actual val hints: List<MultiFactorInfo> = ios.hints().mapNotNull { hint -> (hint as? FIRMultiFactorInfo)?.let { MultiFactorInfo(it) } }
public actual val session: MultiFactorSession = MultiFactorSession(ios.session())

public actual suspend fun resolveSignIn(assertion: MultiFactorAssertion): AuthResult = AuthResult(ios.awaitResult { resolveSignInWithAssertion(assertion.ios, it) })
}
38 changes: 19 additions & 19 deletions firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/user.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,27 @@ public val FirebaseUser.ios: FIRUser get() = ios

public actual class FirebaseUser internal constructor(internal val ios: FIRUser) {
public actual val uid: String
get() = ios.uid
get() = ios.uid()
public actual val displayName: String?
get() = ios.displayName
get() = ios.displayName()
public actual val email: String?
get() = ios.email
get() = ios.email()
public actual val phoneNumber: String?
get() = ios.phoneNumber
get() = ios.phoneNumber()
public actual val photoURL: String?
get() = ios.photoURL?.absoluteString
get() = ios.photoURL()?.absoluteString
public actual val isAnonymous: Boolean
get() = ios.anonymous
get() = ios.anonymous()
public actual val isEmailVerified: Boolean
get() = ios.emailVerified
get() = ios.emailVerified()
public actual val metaData: UserMetaData?
get() = UserMetaData(ios.metadata)
get() = UserMetaData(ios.metadata())
public actual val multiFactor: MultiFactor
get() = MultiFactor(ios.multiFactor)
get() = MultiFactor(ios.multiFactor())
public actual val providerData: List<UserInfo>
get() = ios.providerData.mapNotNull { provider -> (provider as? FIRUserInfoProtocol)?.let { UserInfo(it) } }
get() = ios.providerData().mapNotNull { provider -> (provider as? FIRUserInfoProtocol)?.let { UserInfo(it) } }
public actual val providerId: String
get() = ios.providerID
get() = ios.providerID()

public actual suspend fun delete(): Unit = ios.await { deleteWithCompletion(it) }

Expand Down Expand Up @@ -85,24 +85,24 @@ public val UserInfo.ios: FIRUserInfoProtocol get() = ios

public actual class UserInfo(internal val ios: FIRUserInfoProtocol) {
public actual val displayName: String?
get() = ios.displayName
get() = ios.displayName()
public actual val email: String?
get() = ios.email
get() = ios.email()
public actual val phoneNumber: String?
get() = ios.phoneNumber
get() = ios.phoneNumber()
public actual val photoURL: String?
get() = ios.photoURL?.absoluteString
get() = ios.photoURL()?.absoluteString
public actual val providerId: String
get() = ios.providerID
get() = ios.providerID()
public actual val uid: String
get() = ios.uid
get() = ios.uid()
}

public val UserMetaData.ios: FIRUserMetadata get() = ios

public actual class UserMetaData(internal val ios: FIRUserMetadata) {
public actual val creationTime: Double?
get() = ios.creationDate?.timeIntervalSinceReferenceDate
get() = ios.creationDate()?.timeIntervalSinceReferenceDate
public actual val lastSignInTime: Double?
get() = ios.lastSignInDate?.timeIntervalSinceReferenceDate
get() = ios.lastSignInDate()?.timeIntervalSinceReferenceDate
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package dev.gitlive.firebase.firestore.internal

import com.google.firebase.firestore.FirebaseFirestore
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor

// Since on iOS Callback threads are set as settings, we store the settings explicitly here as well
internal val callbackExecutorMap = ConcurrentHashMap<FirebaseFirestore, Executor>()
internal val callbackExecutorMap = ConcurrentHashMap<com.google.firebase.firestore.FirebaseFirestore, Executor>()
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.gitlive.firebase.firestore

import cocoapods.FirebaseFirestoreInternal.FIRTimestamp
import cocoapods.FirebaseCore.FIRTimestamp
import kotlinx.serialization.Serializable

/** A class representing a platform specific Firebase Timestamp. */
Expand Down
6 changes: 5 additions & 1 deletion firebase-perf/api/android/firebase-perf.api
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ public final class dev/gitlive/firebase/perf/metrics/Trace {
public final fun updateSession (Ldev/gitlive/firebase/perf/session/PerfSession;)V
}

public final class dev/gitlive/firebase/perf/metrics/TraceKt {
public static final fun getAndroid (Ldev/gitlive/firebase/perf/metrics/Trace;)Lcom/google/firebase/perf/metrics/Trace;
}

public final class dev/gitlive/firebase/perf/session/PerfSession {
}

public final class dev/gitlive/firebase/perf/session/PerfSessionKt {
public static final fun getAndroid (Lcom/google/firebase/perf/session/PerfSession;)Lcom/google/firebase/perf/session/PerfSession;
public static final fun getAndroid (Ldev/gitlive/firebase/perf/session/PerfSession;)Lcom/google/firebase/perf/session/PerfSession;
}

Loading

0 comments on commit ecfa47d

Please sign in to comment.