Skip to content

Added firebase-installations #257

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 2 commits into from
Mar 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ tasks {
"firebase-app:updateVersion", "firebase-app:updateDependencyVersion",
"firebase-auth:updateVersion", "firebase-auth:updateDependencyVersion",
"firebase-common:updateVersion", "firebase-common:updateDependencyVersion",
"firebase-config:updateVersion", "firebase-config:updateDependencyVersion",
"firebase-database:updateVersion", "firebase-database:updateDependencyVersion",
"firebase-firestore:updateVersion", "firebase-firestore:updateDependencyVersion",
"firebase-functions:updateVersion", "firebase-functions:updateDependencyVersion",
"firebase-config:updateVersion", "firebase-config:updateDependencyVersion"
"firebase-installations:updateVersion", "firebase-installations:updateDependencyVersion"
)
}
}
Expand Down Expand Up @@ -154,8 +155,8 @@ subprojects {
}
}

if (projectDir.resolve("src/nativeInterop/cinterop/Cartfile").exists()) { // skipping firebase-common module
listOf("bootstrap", "update").forEach {
val carthageTasks = if (projectDir.resolve("src/nativeInterop/cinterop/Cartfile").exists()) { // skipping firebase-common module
listOf("bootstrap", "update").map {
task<Exec>("carthage${it.capitalize()}") {
group = "carthage"
executable = "carthage"
Expand All @@ -166,11 +167,13 @@ subprojects {
)
}
}
}
} else emptyList()

if (Os.isFamily(Os.FAMILY_MAC)) {
withType(org.jetbrains.kotlin.gradle.tasks.CInteropProcess::class) {
dependsOn("carthageBootstrap")
if (carthageTasks.isNotEmpty()) {
dependsOn("carthageBootstrap")
}
}
}

Expand Down
1 change: 0 additions & 1 deletion firebase-app/src/iosMain/c_interop/Cartfile.resolved

This file was deleted.

1 change: 0 additions & 1 deletion firebase-auth/src/iosMain/c_interop/Cartfile.resolved

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -495,4 +495,14 @@ external object firebase {
fun getSource(): String
}
}

fun installations(app: App? = definedExternally): installations.Installations

object installations {
interface Installations {
fun delete(): Promise<Unit>
fun getId(): Promise<String>
fun getToken(forceRefresh: Boolean): Promise<String>
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ package dev.gitlive.firebase

import kotlin.js.Promise

@JsModule("firebase/compat/functions")
@JsName("default")
external object functions

@JsModule("firebase/compat/auth")
@JsName("default")
external object auth
Expand All @@ -22,6 +18,12 @@ external object database
@JsName("default")
external object firestore

@JsModule("firebase/compat/functions")
@JsName("default")
external object functions

external object installations

@JsModule("firebase/compat/remote-config")
@JsName("default")
external object remoteConfig
Expand Down
1 change: 0 additions & 1 deletion firebase-database/src/iosMain/c_interop/Cartfile.resolved

This file was deleted.

1 change: 0 additions & 1 deletion firebase-firestore/src/iosMain/c_interop/Cartfile.resolved

This file was deleted.

1 change: 0 additions & 1 deletion firebase-functions/src/iosMain/c_interop/Cartfile.resolved

This file was deleted.

162 changes: 162 additions & 0 deletions firebase-installations/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license.
*/

import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.konan.target.KonanTarget

version = project.property("firebase-installations.version") as String

plugins {
id("com.android.library")
kotlin("multiplatform")
}

android {
compileSdk = property("targetSdkVersion") as Int
defaultConfig {
minSdk = property("minSdkVersion") as Int
targetSdk = property("targetSdkVersion") as Int
}
sourceSets {
getByName("main") {
manifest.srcFile("src/androidMain/AndroidManifest.xml")
}
}
testOptions {
unitTests.apply {
isIncludeAndroidResources = true
}
}
packagingOptions {
resources.pickFirsts.add("META-INF/kotlinx-serialization-core.kotlin_module")
resources.pickFirsts.add("META-INF/AL2.0")
resources.pickFirsts.add("META-INF/LGPL2.1")
}
lint {
isAbortOnError = false
}
}

val KonanTarget.archVariant: String
get() = if (this is KonanTarget.IOS_X64 || this is KonanTarget.IOS_SIMULATOR_ARM64) {
"ios-arm64_i386_x86_64-simulator"
} else {
"ios-arm64_armv7"
}

kotlin {

android {
publishAllLibraryVariants()
}

val supportIosTarget = project.property("skipIosTarget") != "true"
if (supportIosTarget) {
fun nativeTargetConfig(): KotlinNativeTarget.() -> Unit = {
val nativeFrameworkPaths = listOf(
rootProject.project("firebase-app").projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/iOS")
).plus(
listOf(
"FirebaseAnalytics",
"FirebaseCore",
"FirebaseCoreDiagnostics",
"FirebaseInstallations",
"GoogleAppMeasurement",
"GoogleAppMeasurementIdentitySupport",
"GoogleDataTransport",
"GoogleUtilities",
"nanopb",
"PromisesObjC"
).map {
rootProject.project("firebase-app").projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/${konanTarget.archVariant}")
}
)

binaries {
getTest("DEBUG").apply {
linkerOpts(nativeFrameworkPaths.map { "-F$it" })
linkerOpts("-ObjC")
}
}

compilations.getByName("main") {
cinterops.create("FirebaseInstallations") {
compilerOpts(nativeFrameworkPaths.map { "-F$it" })
extraOpts = listOf("-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=", "-verbose")
}
}
}

ios(configure = nativeTargetConfig())
iosSimulatorArm64(configure = nativeTargetConfig())
}

js {
useCommonJs()
nodejs {
testTask {
useMocha {
timeout = "5s"
}
}
}
browser {
testTask {
useMocha {
timeout = "5s"
}
}
}
}

sourceSets {
all {
languageSettings.apply {
apiVersion = "1.5"
languageVersion = "1.5"
progressiveMode = true
// optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
// optIn("kotlinx.serialization.InternalSerializationApi")
}
}

val commonMain by getting {
dependencies {
api(project(":firebase-app"))
implementation(project(":firebase-common"))
}
}

val androidMain by getting {
dependencies {
api("com.google.firebase:firebase-installations")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbransby Actually there's a separate package for Android, so probably makes sense to leave it as separate module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbransby Can you take a look? I have some more PRs in my pipeline :)

}
}

if (supportIosTarget) {
val iosMain by getting
val iosSimulatorArm64Main by getting
iosSimulatorArm64Main.dependsOn(iosMain)

val iosTest by sourceSets.getting
val iosSimulatorArm64Test by sourceSets.getting
iosSimulatorArm64Test.dependsOn(iosTest)
}

val jsMain by getting
}
}

if (project.property("firebase-installations.skipIosTests") == "true") {
tasks.forEach {
if (it.name.contains("ios", true) && it.name.contains("test", true)) { it.enabled = false }
}
}

signing {
val signingKey: String? by project
val signingPassword: String? by project
useInMemoryPgpKeys(signingKey, signingPassword)
sign(publishing.publications)
}
31 changes: 31 additions & 0 deletions firebase-installations/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@gitlive/firebase-installations",
"version": "1.0.0",
"description": "Wrapper around firebase for usage in Kotlin Multiplatform projects",
"main": "firebase-installations.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/GitLiveApp/firebase-kotlin-sdk.git"
},
"keywords": [
"kotlin",
"multiplatform",
"kotlin-js",
"firebase"
],
"author": "dev.gitlive",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/GitLiveApp/firebase-kotlin-sdk/issues"
},
"homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk",
"dependencies": {
"@gitlive/firebase-app": "1.4.3",
"firebase": "9.4.1",
"kotlin": "1.5.31",
"kotlinx-coroutines-core": "1.5.2"
}
}
1 change: 1 addition & 0 deletions firebase-installations/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest package="dev.gitlive.firebase.installations"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dev.gitlive.firebase.installations

import dev.gitlive.firebase.Firebase
import dev.gitlive.firebase.FirebaseApp
import kotlinx.coroutines.tasks.await

actual val Firebase.installations
get() = FirebaseInstallations(com.google.firebase.installations.FirebaseInstallations.getInstance())

actual fun Firebase.installations(app: FirebaseApp)
= FirebaseInstallations(com.google.firebase.installations.FirebaseInstallations.getInstance(app.android))

actual class FirebaseInstallations internal constructor(val android: com.google.firebase.installations.FirebaseInstallations) {

actual suspend fun delete() = android.delete().await().let { }

actual suspend fun getId(): String = android.id.await()

actual suspend fun getToken(forceRefresh: Boolean): String =
android.getToken(forceRefresh).await().token
}

actual typealias FirebaseInstallationsException = com.google.firebase.installations.FirebaseInstallationsException
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dev.gitlive.firebase.installations

import dev.gitlive.firebase.Firebase
import dev.gitlive.firebase.FirebaseApp
import dev.gitlive.firebase.FirebaseException
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy

expect val Firebase.installations: FirebaseInstallations

expect fun Firebase.installations(app: FirebaseApp): FirebaseInstallations

expect class FirebaseInstallations {
suspend fun delete()
suspend fun getId(): String
suspend fun getToken(forceRefresh: Boolean): String
}

expect class FirebaseInstallationsException: FirebaseException

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package dev.gitlive.firebase.installations

import cocoapods.FirebaseInstallations.*
import dev.gitlive.firebase.Firebase
import dev.gitlive.firebase.FirebaseApp
import dev.gitlive.firebase.FirebaseException
import kotlinx.coroutines.CompletableDeferred
import platform.Foundation.*

actual val Firebase.installations
get() = FirebaseInstallations(FIRInstallations.installations())

actual fun Firebase.installations(app: FirebaseApp)
= FirebaseInstallations(FIRInstallations.installationsWithApp(app.ios))

actual class FirebaseInstallations internal constructor(val ios: FIRInstallations) {

actual suspend fun delete() = ios.await { deleteWithCompletion(completion = it) }

actual suspend fun getId(): String = ios.awaitResult { installationIDWithCompletion(completion = it) }

actual suspend fun getToken(forceRefresh: Boolean): String {
val result: FIRInstallationsAuthTokenResult = ios.awaitResult { authTokenForcingRefresh(forceRefresh = forceRefresh, completion = it) }

return result.authToken
}
}

actual class FirebaseInstallationsException(message: String): FirebaseException(message)

suspend inline fun <T> T.await(function: T.(callback: (NSError?) -> Unit) -> Unit) {
val job = CompletableDeferred<Unit>()
function { error ->
if(error == null) {
job.complete(Unit)
} else {
job.completeExceptionally(FirebaseInstallationsException(error.toString()))
}
}
job.await()
}

suspend inline fun <T, reified R> T.awaitResult(function: T.(callback: (R?, NSError?) -> Unit) -> Unit): R {
val job = CompletableDeferred<R?>()
function { result, error ->
if(error == null) {
job.complete(result)
} else {
job.completeExceptionally(FirebaseInstallationsException(error.toString()))
}
}
return job.await() as R
}
Loading