Skip to content

Commit bf1548b

Browse files
authored
Merge pull request #3 from TeamHubApp/ios-support
add ios implementations
2 parents e7704bc + 9d72403 commit bf1548b

File tree

18 files changed

+622
-591
lines changed

18 files changed

+622
-591
lines changed

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
2-
31
plugins {
42
kotlin("multiplatform") version "1.3.61" apply false
53
}
@@ -48,6 +46,8 @@ subprojects {
4846
"jvmMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3")
4947
"jvmMainApi"("app.teamhub:firebase-java:0.3.0")
5048
"jvmMainApi"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.3")
49+
"iosMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.3")
50+
"iosMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.3.3")
5151
}
5252
}
5353
}

firebase-app/build.gradle.kts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ kotlin {
5353
}
5454

5555
val iosArm64 = iosArm64()
56-
val iosX64 = iosX64()
56+
val iosX64 = iosX64("ios")
5757

5858
sourceSets {
5959
val commonMain by getting {
@@ -69,10 +69,7 @@ kotlin {
6969
val jvmMain by getting {
7070
kotlin.srcDir("src/androidMain/kotlin")
7171
}
72-
val iosMain by creating {
73-
dependencies {
74-
}
75-
}
72+
// val iosMain by creating
7673

7774
configure(listOf(iosArm64, iosX64)) {
7875
compilations.getByName("main") {
Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,43 @@
11
package dev.teamhub.firebase
22

3-
actual class FirebaseApp {
4-
actual val name: String
5-
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
6-
actual val options: FirebaseOptions
7-
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
8-
}
9-
10-
/** Returns the default firebase app instance. */
11-
actual val Firebase.app: FirebaseApp
12-
get() = kotlin.TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
3+
import cocoapods.FirebaseCore.*
134

14-
actual fun Firebase.app(name: String): FirebaseApp = kotlin.TODO("not implemented")
5+
actual open class FirebaseException(message: String) : Exception(message)
6+
actual open class FirebaseNetworkException(message: String) : FirebaseException(message)
7+
actual open class FirebaseTooManyRequestsException(message: String) : FirebaseException(message)
8+
actual open class FirebaseApiNotAvailableException(message: String) : FirebaseException(message)
159

16-
actual fun Firebase.apps(context: Any?): List<FirebaseApp> = kotlin.TODO("not implemented")
17-
18-
actual fun Firebase.initialize(context: Any?): FirebaseApp? = kotlin.TODO("not implemented")
10+
actual val Firebase.app: FirebaseApp
11+
get() = FirebaseApp(FIRApp.defaultApp()!!)
1912

20-
/** Initializes and returns a FirebaseApp. */
21-
actual fun Firebase.initialize(context: Any?, options: FirebaseOptions): FirebaseApp = kotlin.TODO("not implemented")
13+
actual fun Firebase.app(name: String): FirebaseApp =
14+
FirebaseApp(FIRApp.appNamed(name)!!)
2215

23-
/** Initializes and returns a FirebaseApp. */
24-
actual fun Firebase.initialize(context: Any?, options: FirebaseOptions, name: String): FirebaseApp = kotlin.TODO("not implemented")
16+
actual fun Firebase.initialize(context: Any?): FirebaseApp? =
17+
FIRApp.configure().let { app }
2518

26-
actual open class FirebaseException : Exception()
19+
actual fun Firebase.initialize(context: Any?, options: FirebaseOptions, name: String): FirebaseApp =
20+
FIRApp.configureWithName(name, options.toIos()).let { app(name) }
2721

28-
actual class FirebaseNetworkException : FirebaseException()
22+
actual fun Firebase.initialize(context: Any?, options: FirebaseOptions) =
23+
FIRApp.configureWithOptions(options.toIos()).let { app }
2924

30-
actual open class FirebaseTooManyRequestsException : FirebaseException()
25+
actual class FirebaseApp internal constructor(val ios: FIRApp) {
26+
actual val name: String
27+
get() = ios.name
28+
actual val options: FirebaseOptions
29+
get() = ios.options.run { FirebaseOptions(bundleID, APIKey!!, databaseURL!!, trackingID, storageBucket, projectID) }
30+
}
3131

32-
actual open class FirebaseApiNotAvailableException : FirebaseException()
32+
actual fun Firebase.apps(context: Any?) = FIRApp.allApps()!!
33+
.values
34+
.map { FirebaseApp(it as FIRApp) }
35+
36+
private fun FirebaseOptions.toIos() = FIROptions().apply {
37+
bundleID = this@toIos.applicationId
38+
APIKey = this@toIos.apiKey
39+
databaseURL = this@toIos.databaseUrl
40+
trackingID = this@toIos.gaTrackingId
41+
storageBucket = this@toIos.storageBucket
42+
projectID = this@toIos.projectId
43+
}

firebase-auth/build.gradle.kts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ kotlin {
3434
publishLibraryVariants("release", "debug")
3535
}
3636
val iosArm64 = iosArm64()
37-
val iosX64 = iosX64()
37+
val iosX64 = iosX64("ios")
3838
jvm {
3939
val main by compilations.getting {
4040
kotlinOptions {
@@ -63,11 +63,7 @@ kotlin {
6363
api("com.google.firebase:firebase-auth:19.1.0")
6464
}
6565
}
66-
val iosMain by creating {
67-
dependencies {
68-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.3.3")
69-
}
70-
}
66+
// val iosMain by creating
7167
val jvmMain by getting {
7268
kotlin.srcDir("src/androidMain/kotlin")
7369
}
Lines changed: 98 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,124 @@
11
package dev.teamhub.firebase.auth
22

3+
import cocoapods.FirebaseAuth.*
34
import dev.teamhub.firebase.Firebase
45
import dev.teamhub.firebase.FirebaseApp
56
import dev.teamhub.firebase.FirebaseException
7+
import kotlinx.cinterop.*
8+
import kotlinx.coroutines.channels.awaitClose
9+
import kotlinx.coroutines.flow.*
10+
import platform.Foundation.*
11+
import kotlinx.coroutines.CompletableDeferred
612

7-
actual val Firebase.auth: FirebaseAuth
8-
get() = kotlin.TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
913

10-
actual fun Firebase.auth(app: FirebaseApp): FirebaseAuth {
11-
kotlin.TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
12-
}
14+
actual val Firebase.auth
15+
get() = FirebaseAuth(FIRAuth.auth())
16+
17+
actual fun Firebase.auth(app: FirebaseApp) =
18+
FirebaseAuth(FIRAuth.authWithApp(app.ios))
19+
20+
actual class FirebaseAuth internal constructor(val ios: FIRAuth) {
1321

14-
actual class FirebaseAuth {
1522
actual val currentUser: FirebaseUser?
16-
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
17-
actual val authStateChanged: kotlinx.coroutines.flow.Flow<FirebaseUser?>
18-
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
23+
get() = ios.currentUser?.let { FirebaseUser(it) }
1924

20-
actual suspend fun signInWithCustomToken(token: String): AuthResult {
21-
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
22-
}
25+
actual suspend fun signInWithCustomToken(token: String) =
26+
AuthResult(ios.awaitResult { signInWithCustomToken(token, it) })
2327

24-
actual suspend fun signInAnonymously(): AuthResult {
25-
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
26-
}
28+
actual suspend fun signInAnonymously() =
29+
AuthResult(ios.awaitResult { signInAnonymouslyWithCompletion(it) })
30+
31+
actual suspend fun signOut() = ios.throwError { signOut(it) }.run { Unit }
2732

28-
actual suspend fun signOut() {
33+
actual val authStateChanged get() = callbackFlow {
34+
val handle = ios.addAuthStateDidChangeListener { _, user -> offer(user?.let { FirebaseUser(it) }) }
35+
awaitClose { ios.removeAuthStateDidChangeListener(handle) }
2936
}
3037
}
3138

32-
actual class AuthResult {
39+
actual class AuthResult internal constructor(val ios: FIRAuthDataResult) {
3340
actual val user: FirebaseUser?
34-
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
41+
get() = ios.user?.let { FirebaseUser(it) }
3542
}
3643

37-
actual class FirebaseUser {
44+
actual class FirebaseUser internal constructor(val ios: FIRUser) {
3845
actual val uid: String
39-
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
46+
get() = ios.uid
4047
actual val isAnonymous: Boolean
41-
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
48+
get() = ios.isAnonymous()
49+
actual suspend fun delete() = ios.await { deleteWithCompletion(it) }.run { Unit }
50+
actual suspend fun reload() = ios.await { reloadWithCompletion(it) }.run { Unit }
51+
}
52+
53+
actual open class FirebaseAuthException(message: String): FirebaseException(message)
54+
actual open class FirebaseAuthActionCodeException(message: String): FirebaseAuthException(message)
55+
actual open class FirebaseAuthEmailException(message: String): FirebaseAuthException(message)
56+
actual open class FirebaseAuthInvalidCredentialsException(message: String): FirebaseAuthException(message)
57+
actual open class FirebaseAuthInvalidUserException(message: String): FirebaseAuthException(message)
58+
actual open class FirebaseAuthRecentLoginRequiredException(message: String): FirebaseAuthException(message)
59+
actual open class FirebaseAuthUserCollisionException(message: String): FirebaseAuthException(message)
60+
actual open class FirebaseAuthWebException(message: String): FirebaseAuthException(message)
61+
62+
63+
private fun <T, R> T.throwError(block: T.(errorPointer: CPointer<ObjCObjectVar<NSError?>>) -> R): R {
64+
memScoped {
65+
val errorPointer: CPointer<ObjCObjectVar<NSError?>> = alloc<ObjCObjectVar<NSError?>>().ptr
66+
val result = block(errorPointer)
67+
val error: NSError? = errorPointer.pointed.value
68+
if (error != null) {
69+
throw error.toException()
70+
}
71+
return result
72+
}
73+
}
4274

43-
actual suspend fun delete() {
75+
private suspend fun <T, R> T.awaitResult(function: T.(callback: (R?, NSError?) -> Unit) -> Unit): R {
76+
val job = CompletableDeferred<R>()
77+
function { result, error ->
78+
if(result != null) {
79+
job.complete(result)
80+
} else if(error != null) {
81+
job.completeExceptionally(error.toException())
82+
}
4483
}
84+
return job.await()
85+
}
4586

46-
actual suspend fun reload() {
87+
private suspend fun <T> T.await(function: T.(callback: (NSError?) -> Unit) -> Unit) {
88+
val job = CompletableDeferred<Unit>()
89+
function { error ->
90+
if(error == null) {
91+
job.complete(Unit)
92+
} else {
93+
job.completeExceptionally(error.toException())
94+
}
4795
}
96+
job.await()
4897
}
4998

50-
actual open class FirebaseAuthException : FirebaseException()
51-
actual class FirebaseAuthActionCodeException : FirebaseAuthException()
52-
actual class FirebaseAuthEmailException : FirebaseAuthException()
53-
actual class FirebaseAuthInvalidCredentialsException : FirebaseAuthException()
54-
actual class FirebaseAuthInvalidUserException : FirebaseAuthException()
55-
actual class FirebaseAuthRecentLoginRequiredException : FirebaseAuthException()
56-
actual class FirebaseAuthUserCollisionException : FirebaseAuthException()
57-
actual class FirebaseAuthWebException : FirebaseAuthException()
99+
private fun NSError.toException() = when(domain) {
100+
FIRAuthErrorDomain -> when(code) {
101+
FIRAuthErrorCodeInvalidActionCode,
102+
FIRAuthErrorCodeExpiredActionCode -> FirebaseAuthActionCodeException(toString())
103+
104+
FIRAuthErrorCodeInvalidEmail,
105+
FIRAuthErrorCodeEmailAlreadyInUse -> FirebaseAuthEmailException(toString())
106+
107+
FIRAuthErrorCodeInvalidCredential -> FirebaseAuthInvalidCredentialsException(toString())
108+
109+
FIRAuthErrorCodeInvalidUserToken -> FirebaseAuthInvalidUserException(toString())
110+
111+
FIRAuthErrorCodeRequiresRecentLogin -> FirebaseAuthRecentLoginRequiredException(toString())
112+
113+
FIRAuthErrorCodeEmailAlreadyInUse,
114+
FIRAuthErrorCodeAccountExistsWithDifferentCredential,
115+
FIRAuthErrorCodeCredentialAlreadyInUse -> FirebaseAuthUserCollisionException(toString())
116+
117+
FIRAuthErrorCodeWebContextAlreadyPresented,
118+
FIRAuthErrorCodeWebContextCancelled,
119+
FIRAuthErrorCodeWebInternalError -> FirebaseAuthWebException(toString())
120+
121+
else -> FirebaseAuthException(toString())
122+
}
123+
else -> FirebaseAuthException(toString())
124+
}

firebase-auth/src/jsMain/kotlin/dev/teamhub/firebase/auth/auth.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,4 @@ private fun errorToException(cause: Throwable) = when(val code = cause.asDynamic
8282
// "auth/too-many-arguments" ->
8383
// "auth/unauthorized-domain" ->
8484
else -> FirebaseAuthException(code, cause)
85-
}
85+
}

firebase-common/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ kotlin {
3939
}
4040

4141
val iosArm64 = iosArm64()
42-
val iosX64 = iosX64()
42+
val iosX64 = iosX64("ios")
4343

4444
jvm {
4545
val main by compilations.getting {
@@ -87,7 +87,7 @@ kotlin {
8787
}
8888
kotlin.srcDir("src/androidTest/kotlin")
8989
}
90-
val iosMain by creating {
90+
val iosMain by getting {
9191
dependencies {
9292
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.14.0")
9393
}

firebase-common/src/iosMain/c_interop/FirebaseCore.def

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

firebase-database/build.gradle.kts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ kotlin {
4141
}
4242
}
4343
val iosArm64 = iosArm64()
44-
val iosX64 = iosX64()
44+
val iosX64 = iosX64("ios")
4545

4646
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>> {
4747
kotlinOptions.freeCompilerArgs += listOf(
@@ -76,12 +76,7 @@ kotlin {
7676
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:0.14.0")
7777
}
7878
}
79-
val iosMain by creating {
80-
dependencies {
81-
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.14.0")
82-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.3.3")
83-
}
84-
}
79+
// val iosMain by creating
8580

8681
configure(listOf(iosArm64, iosX64)) {
8782
compilations.getByName("main") {

0 commit comments

Comments
 (0)