diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e0ae15d91..955306bf0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -27,12 +27,66 @@ jobs: run: chmod +x gradlew - name: Install Carthage run: brew install carthage - - name: Publish + - name: Publish Firebase App uses: eskatos/gradle-command-action@v1 with: - arguments: publish + arguments: :firebase-app:publish env: sonatypeUsername: ${{ secrets.SONATYPEUSERNAME }} sonatypePassword: ${{ secrets.SONATYPEPASSWORD }} ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PASSPHRASE }} + - name: Publish Firebase Auth + uses: eskatos/gradle-command-action@v1 + with: + arguments: :firebase-auth:publish + env: + sonatypeUsername: ${{ secrets.SONATYPEUSERNAME }} + sonatypePassword: ${{ secrets.SONATYPEPASSWORD }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PASSPHRASE }} + - name: Publish Firebase Common + uses: eskatos/gradle-command-action@v1 + with: + arguments: :firebase-common:publish + env: + sonatypeUsername: ${{ secrets.SONATYPEUSERNAME }} + sonatypePassword: ${{ secrets.SONATYPEPASSWORD }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PASSPHRASE }} + - name: Publish Firebase Config + uses: eskatos/gradle-command-action@v1 + with: + arguments: :firebase-config:publish + env: + sonatypeUsername: ${{ secrets.SONATYPEUSERNAME }} + sonatypePassword: ${{ secrets.SONATYPEPASSWORD }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PASSPHRASE }} + - name: Publish Firebase Database + uses: eskatos/gradle-command-action@v1 + with: + arguments: :firebase-database:publish + env: + sonatypeUsername: ${{ secrets.SONATYPEUSERNAME }} + sonatypePassword: ${{ secrets.SONATYPEPASSWORD }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PASSPHRASE }} + - name: Publish Firebase Firestore + uses: eskatos/gradle-command-action@v1 + with: + arguments: :firebase-firestore:publish + env: + sonatypeUsername: ${{ secrets.SONATYPEUSERNAME }} + sonatypePassword: ${{ secrets.SONATYPEPASSWORD }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PASSPHRASE }} + - name: Publish Firebase Functions + uses: eskatos/gradle-command-action@v1 + with: + arguments: :firebase-functions:publish + env: + sonatypeUsername: ${{ secrets.SONATYPEUSERNAME }} + sonatypePassword: ${{ secrets.SONATYPEPASSWORD }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PASSPHRASE }} \ No newline at end of file diff --git a/README.md b/README.md index cee392130..d0d1bbeb0 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,13 @@ The following libraries are available for the various Firebase products. | Service or Product | Gradle Dependency | API Coverage | | ------------------------------------------------------------------------------------ | :-----------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [Authentication](https://firebase.google.com/docs/auth#kotlin-android) | [`dev.gitlive:firebase-auth:1.4.1`](https://search.maven.org/artifact/dev.gitlive/firebase-auth/1.4.1/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt) | -| [Realtime Database](https://firebase.google.com/docs/database#kotlin-android) | [`dev.gitlive:firebase-database:1.4.1`](https://search.maven.org/artifact/dev.gitlive/firebase-database/1.4.1/pom) | [![70%](https://img.shields.io/badge/-70%25-orange?style=flat-square)](/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt) | -| [Cloud Firestore](https://firebase.google.com/docs/firestore#kotlin-android) | [`dev.gitlive:firebase-firestore:1.4.1`](https://search.maven.org/artifact/dev.gitlive/firebase-firestore/1.4.1/pom) | [![60%](https://img.shields.io/badge/-60%25-orange?style=flat-square)](/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt) | -| [Cloud Functions](https://firebase.google.com/docs/functions/callable#kotlin-android)| [`dev.gitlive:firebase-functions:1.4.1`](https://search.maven.org/artifact/dev.gitlive/firebase-functions/1.4.1/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-functions/src/commonMain/kotlin/dev/gitlive/firebase/functions/functions.kt) | -| [Cloud Messaging](https://firebase.google.com/docs/messaging#kotlin-android) | [`dev.gitlive:firebase-messaging:1.4.1`](https://search.maven.org/artifact/dev.gitlive/firebase-messaging/1.4.1/pom) | ![0%](https://img.shields.io/badge/-0%25-lightgrey?style=flat-square) | -| [Cloud Storage](https://firebase.google.com/docs/storage#kotlin-android) | [`dev.gitlive:firebase-storage:1.4.1`](https://search.maven.org/artifact/dev.gitlive/firebase-storage/1.4.1/pom) | ![0%](https://img.shields.io/badge/-0%25-lightgrey?style=flat-square) | -| [Remote Config](https://firebase.google.com/docs/remote-config/get-started?platform=android) | [`dev.gitlive:firebase-config:1.4.1`](https://search.maven.org/artifact/dev.gitlive/firebase-config/1.4.1/pom) | ![20%](https://img.shields.io/badge/-20%25-orange?style=flat-square) | +| [Authentication](https://firebase.google.com/docs/auth#kotlin-android) | [`dev.gitlive:firebase-auth:1.4.3`](https://search.maven.org/artifact/dev.gitlive/firebase-auth/1.4.3/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt) | +| [Realtime Database](https://firebase.google.com/docs/database#kotlin-android) | [`dev.gitlive:firebase-database:1.4.3`](https://search.maven.org/artifact/dev.gitlive/firebase-database/1.4.3/pom) | [![70%](https://img.shields.io/badge/-70%25-orange?style=flat-square)](/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt) | +| [Cloud Firestore](https://firebase.google.com/docs/firestore#kotlin-android) | [`dev.gitlive:firebase-firestore:1.4.3`](https://search.maven.org/artifact/dev.gitlive/firebase-firestore/1.4.3/pom) | [![60%](https://img.shields.io/badge/-60%25-orange?style=flat-square)](/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt) | +| [Cloud Functions](https://firebase.google.com/docs/functions/callable#kotlin-android)| [`dev.gitlive:firebase-functions:1.4.3`](https://search.maven.org/artifact/dev.gitlive/firebase-functions/1.4.3/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-functions/src/commonMain/kotlin/dev/gitlive/firebase/functions/functions.kt) | +| [Cloud Messaging](https://firebase.google.com/docs/messaging#kotlin-android) | [`dev.gitlive:firebase-messaging:1.4.3`](https://search.maven.org/artifact/dev.gitlive/firebase-messaging/1.4.3/pom) | ![0%](https://img.shields.io/badge/-0%25-lightgrey?style=flat-square) | +| [Cloud Storage](https://firebase.google.com/docs/storage#kotlin-android) | [`dev.gitlive:firebase-storage:1.4.3`](https://search.maven.org/artifact/dev.gitlive/firebase-storage/1.4.3/pom) | ![0%](https://img.shields.io/badge/-0%25-lightgrey?style=flat-square) | +| [Remote Config](https://firebase.google.com/docs/remote-config/get-started?platform=android) | [`dev.gitlive:firebase-config:1.4.3`](https://search.maven.org/artifact/dev.gitlive/firebase-config/1.4.3/pom) | ![20%](https://img.shields.io/badge/-20%25-orange?style=flat-square) | @@ -66,7 +66,7 @@ The Firebase Kotlin SDK uses Kotlin serialization to read and write custom class ```groovy plugins { kotlin("multiplatform") // or kotlin("jvm") or any other kotlin plugin - kotlin("plugin.serialization") version "1.5.10" + kotlin("plugin.serialization") version "1.5.30" } ``` @@ -166,13 +166,13 @@ If you are building a Kotlin multiplatform library which will be consumed from J ```json "dependencies": { - "@gitlive/firebase-auth": "1.4.1", - "@gitlive/firebase-database": "1.4.1", - "@gitlive/firebase-firestore": "1.4.1", - "@gitlive/firebase-functions": "1.4.1", - "@gitlive/firebase-storage": "1.4.1", - "@gitlive/firebase-messaging": "1.4.1", - "@gitlive/firebase-config": "1.4.1" + "@gitlive/firebase-auth": "1.4.3", + "@gitlive/firebase-database": "1.4.3", + "@gitlive/firebase-firestore": "1.4.3", + "@gitlive/firebase-functions": "1.4.3", + "@gitlive/firebase-storage": "1.4.3", + "@gitlive/firebase-messaging": "1.4.3", + "@gitlive/firebase-config": "1.4.3" } ``` diff --git a/build.gradle.kts b/build.gradle.kts index bf5a782b3..f4cbeeaea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,12 @@ -import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties import org.apache.tools.ant.taskdefs.condition.Os import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { - kotlin("multiplatform") version "1.5.21" apply false + kotlin("multiplatform") version "1.5.31" apply false id("base") + id("maven-publish") + kotlin("native.cocoapods") } buildscript { @@ -18,22 +19,13 @@ buildscript { } } dependencies { - classpath("com.android.tools.build:gradle:7.0.0") + classpath("com.android.tools.build:gradle:7.0.3") classpath("com.adarshr:gradle-test-logger-plugin:2.1.1") } } val targetSdkVersion by extra(30) -val minSdkVersion by extra(16) - -// TODO: Hierarchical project structures are not fully supported in IDEA, enable only for a regular built (https://youtrack.jetbrains.com/issue/KT-35011) -// add idea.active=true for local development -val _ideaActive = gradleLocalProperties(rootDir)["idea.active"] == "true" - -//if (!_ideaActive) { -// ext["kotlin.mpp.enableGranularSourceSetsMetadata"] = "true" -// ext["kotlin.native.enableDependencyPropagation"] = "false" -//} +val minSdkVersion by extra(19) tasks { val updateVersions by registering { @@ -51,8 +43,6 @@ tasks { subprojects { - val ideaActive by extra(_ideaActive) - group = "dev.gitlive" apply(plugin="com.adarshr.test-logger") @@ -174,8 +164,7 @@ subprojects { args( it, "--project-directory", projectDir.resolve("src/nativeInterop/cinterop"), - "--platform", "iOS", - "--cache-builds" + "--platform", "iOS" ) } } @@ -207,12 +196,12 @@ subprojects { } dependencies { - "commonMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1") - "androidMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.5.1") - "androidMainImplementation"(platform("com.google.firebase:firebase-bom:28.3.1")) + "commonMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") + "androidMainImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.5.2") + "androidMainImplementation"(platform("com.google.firebase:firebase-bom:29.0.0")) "commonTestImplementation"(kotlin("test-common")) "commonTestImplementation"(kotlin("test-annotations-common")) - "commonTestImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1") + "commonTestImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") "jsTestImplementation"(kotlin("test-js")) "androidAndroidTestImplementation"(kotlin("test-junit")) "androidAndroidTestImplementation"("junit:junit:4.13.2") diff --git a/firebase-app/build.gradle.kts b/firebase-app/build.gradle.kts index b9bbd9f7c..594414052 100644 --- a/firebase-app/build.gradle.kts +++ b/firebase-app/build.gradle.kts @@ -44,52 +44,59 @@ android { } } +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() } - fun nativeTargetConfig(): KotlinNativeTarget.() -> Unit = { - val nativeFrameworkPaths = listOf( - projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/iOS") - ).plus( - listOf( - "FirebaseAnalytics", - "FirebaseCore", - "FirebaseCoreDiagnostics", - "FirebaseInstallations", - "GoogleAppMeasurement", - "GoogleDataTransport", - "GoogleUtilities", - "nanopb", - "PromisesObjC" - ).map { - val archVariant = if (konanTarget is KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - - projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ) + val supportIosTarget = project.property("skipIosTarget") != "true" + if (supportIosTarget) { + + fun nativeTargetConfig(): KotlinNativeTarget.() -> Unit = { + val nativeFrameworkPaths = listOf( + projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/iOS") + ).plus( + listOf( + "FirebaseAnalytics", + "FirebaseCore", + "FirebaseCoreDiagnostics", + "FirebaseInstallations", + "GoogleAppMeasurement", + "GoogleAppMeasurementIdentitySupport", + "GoogleDataTransport", + "GoogleUtilities", + "nanopb", + "PromisesObjC" + ).map { + projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/${konanTarget.archVariant}") + } + ) - binaries { - getTest("DEBUG").apply { - linkerOpts(nativeFrameworkPaths.map { "-F$it" }) - linkerOpts("-ObjC") + binaries { + getTest("DEBUG").apply { + linkerOpts(nativeFrameworkPaths.map { "-F$it" }) + linkerOpts("-ObjC") + } } - } - compilations.getByName("main") { - cinterops.create("FirebaseCore") { - compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + compilations.getByName("main") { + cinterops.create("FirebaseCore") { + compilerOpts(nativeFrameworkPaths.map { "-F$it" }) + extraOpts = listOf("-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=", "-verbose") + } } } - } - if (project.extra["ideaActive"] as Boolean) { - iosX64("ios", nativeTargetConfig()) - } else { ios(configure = nativeTargetConfig()) + iosSimulatorArm64(configure = nativeTargetConfig()) } js { @@ -127,16 +134,30 @@ kotlin { val androidMain by getting { dependencies { - api("com.google.firebase:firebase-common-ktx") + api("com.google.firebase:firebase-common") } } - val iosMain by getting + 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-app.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 diff --git a/firebase-app/package.json b/firebase-app/package.json index cd1916bca..5dd434369 100644 --- a/firebase-app/package.json +++ b/firebase-app/package.json @@ -23,9 +23,9 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-common": "1.4.1", - "firebase": "8.8.1", - "kotlin": "1.5.21", - "kotlinx-coroutines-core": "1.5.1" + "@gitlive/firebase-common": "1.4.4", + "firebase": "9.4.1", + "kotlin": "1.5.31", + "kotlinx-coroutines-core": "1.5.2" } } diff --git a/firebase-app/src/jsTest/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/jsTest/kotlin/dev/gitlive/firebase/firebase.kt index 0a732735e..0e8a91771 100644 --- a/firebase-app/src/jsTest/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/jsTest/kotlin/dev/gitlive/firebase/firebase.kt @@ -14,7 +14,7 @@ actual fun runTest(test: suspend () -> Unit) = GlobalScope try { test() } catch (e: dynamic) { - e.log() + (e as? Throwable)?.log() throw e } }.asDynamic() diff --git a/firebase-app/src/nativeInterop/cinterop/Cartfile b/firebase-app/src/nativeInterop/cinterop/Cartfile index e0f994544..8d7d98311 100644 --- a/firebase-app/src/nativeInterop/cinterop/Cartfile +++ b/firebase-app/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" == 8.5.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" == 8.9.1 diff --git a/firebase-app/src/nativeInterop/cinterop/FirebaseCore.def b/firebase-app/src/nativeInterop/cinterop/FirebaseCore.def index 4287b7982..ae9ff7d73 100644 --- a/firebase-app/src/nativeInterop/cinterop/FirebaseCore.def +++ b/firebase-app/src/nativeInterop/cinterop/FirebaseCore.def @@ -2,4 +2,4 @@ language = Objective-C package = cocoapods.FirebaseCore modules = FirebaseCore compilerOpts = -framework FirebaseCore -linkerOpts = -framework FirebaseCore -framework FirebaseCoreDiagnostics -framework FirebaseAnalytics -framework GoogleAppMeasurement -framework FirebaseInstallations -framework GoogleDataTransport -framework GoogleUtilities -framework PromisesObjC -framework nanopb -framework StoreKit -lsqlite3 +linkerOpts = -framework FirebaseAnalytics -framework FirebaseCore -framework FirebaseCoreDiagnostics -framework FirebaseInstallations -framework GoogleAppMeasurement -framework GoogleAppMeasurementIdentitySupport -framework GoogleDataTransport -framework GoogleUtilities -framework nanopb -framework PromisesObjC -framework StoreKit -lsqlite3 diff --git a/firebase-auth/build.gradle.kts b/firebase-auth/build.gradle.kts index 51bff83d9..eef472fbe 100644 --- a/firebase-auth/build.gradle.kts +++ b/firebase-auth/build.gradle.kts @@ -67,61 +67,65 @@ android { // logEmulatorOutput(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() } - 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", - "GoogleDataTransport", - "GoogleUtilities", - "nanopb", - "PromisesObjC" - ).map { - val archVariant = if (konanTarget is KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - - rootProject.project("firebase-app").projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ).plus( - listOf( - "FirebaseAuth", - "GTMSessionFetcher" - ).map { - val archVariant = if (konanTarget is KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - - projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ) - - binaries { - getTest("DEBUG").apply { - linkerOpts(nativeFrameworkPaths.map { "-F$it" }) - linkerOpts("-ObjC") + 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}") + } + ).plus( + listOf( + "FirebaseAuth", + "GTMSessionFetcher" + ).map { + 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("FirebaseAuth") { - compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + compilations.getByName("main") { + cinterops.create("FirebaseAuth") { + compilerOpts(nativeFrameworkPaths.map { "-F$it" }) + extraOpts = listOf("-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=", "-verbose") + } } } - } - if (project.extra["ideaActive"] as Boolean) { - iosX64("ios", nativeTargetConfig()) - } else { ios(configure = nativeTargetConfig()) + iosSimulatorArm64(configure = nativeTargetConfig()) } js { @@ -148,7 +152,7 @@ kotlin { apiVersion = "1.5" languageVersion = "1.5" progressiveMode = true - useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi") + optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") } } @@ -161,16 +165,30 @@ kotlin { val androidMain by getting { dependencies { - api("com.google.firebase:firebase-auth-ktx") + api("com.google.firebase:firebase-auth") } } - val iosMain by getting + 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-auth.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 diff --git a/firebase-auth/package.json b/firebase-auth/package.json index 393cdbfbb..60ece0d84 100644 --- a/firebase-auth/package.json +++ b/firebase-auth/package.json @@ -23,9 +23,9 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "1.4.1", - "firebase": "8.8.1", - "kotlin": "1.5.21", - "kotlinx-coroutines-core": "1.5.1" + "@gitlive/firebase-app": "1.4.4", + "firebase": "9.4.1", + "kotlin": "1.5.31", + "kotlinx-coroutines-core": "1.5.2" } } diff --git a/firebase-auth/src/androidAndroidTest/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/androidAndroidTest/kotlin/dev/gitlive/firebase/auth/auth.kt index b6495fc34..364e7f381 100644 --- a/firebase-auth/src/androidAndroidTest/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/androidAndroidTest/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -13,14 +13,7 @@ actual val emulatorHost: String = "10.0.2.2" actual val context: Any = InstrumentationRegistry.getInstrumentation().targetContext -actual val currentPlatform: Platform = Platform.Android - -actual fun runTest(skip: Boolean, test: suspend () -> Unit) = runBlocking { - if (skip) { - Log.w("Test", "Skip the test.") - return@runBlocking - } - +actual fun runTest(test: suspend () -> Unit) = runBlocking { test() } diff --git a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt index d23af170b..6f7c09ab2 100644 --- a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -66,6 +66,8 @@ actual class FirebaseAuth internal constructor(val android: com.google.firebase. actual suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) = android.sendSignInLinkToEmail(email, actionCodeSettings.toAndroid()).await().run { Unit } + actual fun isSignInWithEmailLink(link: String) = android.isSignInWithEmailLink(link) + actual suspend fun signInWithEmailAndPassword(email: String, password: String) = AuthResult(android.signInWithEmailAndPassword(email, password).await()) @@ -77,6 +79,9 @@ actual class FirebaseAuth internal constructor(val android: com.google.firebase. actual suspend fun signInWithCredential(authCredential: AuthCredential) = AuthResult(android.signInWithCredential(authCredential.android).await()) + actual suspend fun signInWithEmailLink(email: String, link: String) = + AuthResult(android.signInWithEmailLink(email, link).await()) + actual suspend fun signOut() = android.signOut() actual suspend fun updateCurrentUser(user: FirebaseUser) = android.updateCurrentUser(user.android).await().run { Unit } diff --git a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/credentials.kt b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/credentials.kt index 02fcc5d9b..4e0653ab2 100644 --- a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/credentials.kt +++ b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/credentials.kt @@ -39,7 +39,12 @@ actual object GithubAuthProvider { } actual object GoogleAuthProvider { - actual fun credential(idToken: String, accessToken: String): AuthCredential = AuthCredential(com.google.firebase.auth.GoogleAuthProvider.getCredential(idToken, accessToken)) + actual fun credential(idToken: String?, accessToken: String?): AuthCredential { + require(idToken != null || accessToken != null) { + "Both parameters are optional but at least one must be present." + } + return AuthCredential(com.google.firebase.auth.GoogleAuthProvider.getCredential(idToken, accessToken)) + } } actual class OAuthProvider(val android: com.google.firebase.auth.OAuthProvider) { diff --git a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt index ea9981b40..c27b137ac 100644 --- a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -27,10 +27,12 @@ expect class FirebaseAuth { suspend fun fetchSignInMethodsForEmail(email: String): List suspend fun sendPasswordResetEmail(email: String, actionCodeSettings: ActionCodeSettings? = null) suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) + fun isSignInWithEmailLink(link: String): Boolean suspend fun signInWithEmailAndPassword(email: String, password: String): AuthResult suspend fun signInWithCustomToken(token: String): AuthResult suspend fun signInAnonymously(): AuthResult suspend fun signInWithCredential(authCredential: AuthCredential): AuthResult + suspend fun signInWithEmailLink(email: String, link: String): AuthResult suspend fun signOut() suspend fun updateCurrentUser(user: FirebaseUser) suspend fun verifyPasswordResetCode(code: String): String @@ -67,7 +69,11 @@ data class ActionCodeSettings( val iOSBundleId: String? = null ) -data class AndroidPackageName(val packageName: String, val installIfNotAvailable: Boolean, val minimumVersion: String?) +data class AndroidPackageName( + val packageName: String, + val installIfNotAvailable: Boolean = true, + val minimumVersion: String? = null +) expect open class FirebaseAuthException : FirebaseException expect class FirebaseAuthActionCodeException : FirebaseAuthException diff --git a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/credentials.kt b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/credentials.kt index d0fcf08f0..ae23f85ab 100644 --- a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/credentials.kt +++ b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/credentials.kt @@ -26,7 +26,7 @@ expect object GithubAuthProvider { } expect object GoogleAuthProvider { - fun credential(idToken: String, accessToken: String): AuthCredential + fun credential(idToken: String?, accessToken: String?): AuthCredential } expect class OAuthProvider constructor( diff --git a/firebase-auth/src/commonTest/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/commonTest/kotlin/dev/gitlive/firebase/auth/auth.kt index 9a94d09e9..d4accec09 100644 --- a/firebase-auth/src/commonTest/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/commonTest/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -10,16 +10,10 @@ import kotlin.test.* expect val emulatorHost: String expect val context: Any -expect fun runTest(skip: Boolean = false, test: suspend () -> Unit) -expect val currentPlatform: Platform - -enum class Platform { Android, IOS, JS } +expect fun runTest(test: suspend () -> Unit) class FirebaseAuthTest { - // Skip the tests on iOS simulator due keychain exceptions - private val skip = currentPlatform == Platform.IOS - @BeforeTest fun initializeFirebase() { Firebase @@ -41,14 +35,14 @@ class FirebaseAuthTest { } @Test - fun testSignInWithUsernameAndPassword() = runTest(skip) { + fun testSignInWithUsernameAndPassword() = runTest { val uid = getTestUid("test@test.com", "test123") val result = Firebase.auth.signInWithEmailAndPassword("test@test.com", "test123") assertEquals(uid, result.user!!.uid) } @Test - fun testCreateUserWithEmailAndPassword() = runTest(skip) { + fun testCreateUserWithEmailAndPassword() = runTest { val email = "test+${Random.nextInt(100000)}@test.com" val createResult = Firebase.auth.createUserWithEmailAndPassword(email, "test123") assertNotEquals(null, createResult.user?.uid) @@ -63,7 +57,7 @@ class FirebaseAuthTest { } @Test - fun testFetchSignInMethods() = runTest(skip) { + fun testFetchSignInMethods() = runTest { val email = "test+${Random.nextInt(100000)}@test.com" var signInMethodResult = Firebase.auth.fetchSignInMethodsForEmail(email) assertEquals(emptyList(), signInMethodResult) @@ -75,7 +69,7 @@ class FirebaseAuthTest { } @Test - fun testSendEmailVerification() = runTest(skip) { + fun testSendEmailVerification() = runTest { val email = "test+${Random.nextInt(100000)}@test.com" val createResult = Firebase.auth.createUserWithEmailAndPassword(email, "test123") assertNotEquals(null, createResult.user?.uid) @@ -85,7 +79,7 @@ class FirebaseAuthTest { } @Test - fun sendPasswordResetEmail() = runTest(skip) { + fun sendPasswordResetEmail() = runTest { val email = "test+${Random.nextInt(100000)}@test.com" val createResult = Firebase.auth.createUserWithEmailAndPassword(email, "test123") assertNotEquals(null, createResult.user?.uid) @@ -96,13 +90,21 @@ class FirebaseAuthTest { } @Test - fun testSignInWithCredential() = runTest(skip) { + fun testSignInWithCredential() = runTest { val uid = getTestUid("test@test.com", "test123") val credential = EmailAuthProvider.credential("test@test.com", "test123") val result = Firebase.auth.signInWithCredential(credential) assertEquals(uid, result.user!!.uid) } + @Test + fun testIsSignInWithEmailLink() { + val validLink = "http://localhost:9099/emulator/action?mode=signIn&lang=en&oobCode=_vr0QcFcxcVeLZbrcU-GpTaZiuxlHquqdC8MSy0YM_vzWCTAQgV9Jq&apiKey=fake-api-key&continueUrl=https%3A%2F%2Fexample.com%2Fsignin" + val invalidLink = "http://localhost:9099/emulator/action?mode=signIn&lang=en&&apiKey=fake-api-key&continueUrl=https%3A%2F%2Fexample.com%2Fsignin" + assertTrue(Firebase.auth.isSignInWithEmailLink(validLink)) + assertFalse(Firebase.auth.isSignInWithEmailLink(invalidLink)) + } + private suspend fun getTestUid(email: String, password: String): String { val uid = Firebase.auth.let { val user = try { diff --git a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt index 9c83e881e..96e5a152e 100644 --- a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -58,6 +58,8 @@ actual class FirebaseAuth internal constructor(val ios: FIRAuth) { actual suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) = ios.await { sendSignInLinkToEmail(email, actionCodeSettings.toIos(), it) }.run { Unit } + actual fun isSignInWithEmailLink(link: String) = ios.isSignInWithEmailLink(link) + actual suspend fun signInWithEmailAndPassword(email: String, password: String) = AuthResult(ios.awaitResult { signInWithEmail(email = email, password = password, completion = it) }) @@ -70,6 +72,9 @@ actual class FirebaseAuth internal constructor(val ios: FIRAuth) { actual suspend fun signInWithCredential(authCredential: AuthCredential) = AuthResult(ios.awaitResult { signInWithCredential(authCredential.ios, it) }) + actual suspend fun signInWithEmailLink(email: String, link: String) = + AuthResult(ios.awaitResult { signInWithEmail(email = email, link = link, completion = it) }) + actual suspend fun signOut() = ios.throwError { signOut(it) }.run { Unit } actual suspend fun updateCurrentUser(user: FirebaseUser) = ios.await { updateCurrentUser(user.ios, it) }.run { Unit } diff --git a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/credentials.kt b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/credentials.kt index 7702b0118..f20b884e1 100644 --- a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/credentials.kt +++ b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/credentials.kt @@ -31,7 +31,11 @@ actual object GithubAuthProvider { } actual object GoogleAuthProvider { - actual fun credential(idToken: String, accessToken: String): AuthCredential = AuthCredential(FIRGoogleAuthProvider.credentialWithIDToken(idToken, accessToken)) + actual fun credential(idToken: String?, accessToken: String?): AuthCredential { + requireNotNull(idToken) { "idToken must not be null" } + requireNotNull(accessToken) { "accessToken must not be null" } + return AuthCredential(FIRGoogleAuthProvider.credentialWithIDToken(idToken, accessToken)) + } } actual class OAuthProvider(val ios: FIROAuthProvider) { @@ -80,4 +84,4 @@ actual interface PhoneVerificationProvider { actual object TwitterAuthProvider { actual fun credential(token: String, secret: String): AuthCredential = AuthCredential(FIRTwitterAuthProvider.credentialWithToken(token, secret)) -} \ No newline at end of file +} diff --git a/firebase-auth/src/iosTest/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/iosTest/kotlin/dev/gitlive/firebase/auth/auth.kt index bd72b1dbe..540531237 100644 --- a/firebase-auth/src/iosTest/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/iosTest/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -11,14 +11,7 @@ actual val emulatorHost: String = "localhost" actual val context: Any = Unit -actual val currentPlatform: Platform = Platform.IOS - -actual fun runTest(skip: Boolean, test: suspend () -> Unit) = runBlocking { - if (skip) { - NSLog("Skip the test.") - return@runBlocking - } - +actual fun runTest(test: suspend () -> Unit) = runBlocking { val testRun = MainScope().async { test() } while (testRun.isActive) { NSRunLoop.mainRunLoop.runMode( diff --git a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/auth.kt index 50f97e2c4..70d3ea304 100644 --- a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -53,6 +53,8 @@ actual class FirebaseAuth internal constructor(val js: firebase.auth.Auth) { actual suspend fun sendSignInLinkToEmail(email: String, actionCodeSettings: ActionCodeSettings) = rethrow { js.sendSignInLinkToEmail(email, actionCodeSettings.toJson()).await() } + actual fun isSignInWithEmailLink(link: String) = rethrow { js.isSignInWithEmailLink(link) } + actual suspend fun signInWithEmailAndPassword(email: String, password: String) = rethrow { AuthResult(js.signInWithEmailAndPassword(email, password).await()) } @@ -65,6 +67,9 @@ actual class FirebaseAuth internal constructor(val js: firebase.auth.Auth) { actual suspend fun signInWithCredential(authCredential: AuthCredential) = rethrow { AuthResult(js.signInWithCredential(authCredential.js).await()) } + actual suspend fun signInWithEmailLink(email: String, link: String) = + rethrow { AuthResult(js.signInWithEmailLink(email, link).await()) } + actual suspend fun signOut() = rethrow { js.signOut().await() } actual suspend fun updateCurrentUser(user: FirebaseUser) = @@ -119,6 +124,7 @@ actual class AuthTokenResult(val js: firebase.auth.IdTokenResult) { } internal fun ActionCodeSettings.toJson() = json( + "url" to url, "android" to (androidPackageName?.run { json("installApp" to installIfNotAvailable, "minimumVersion" to minimumVersion, "packageName" to packageName) } ?: undefined), "dynamicLinkDomain" to (dynamicLinkDomain ?: undefined), "handleCodeInApp" to canHandleCodeInApp, diff --git a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/credentials.kt b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/credentials.kt index 0074b5704..f5f911cc1 100644 --- a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/credentials.kt +++ b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/credentials.kt @@ -29,8 +29,12 @@ actual object GithubAuthProvider { } actual object GoogleAuthProvider { - actual fun credential(idToken: String, accessToken: String): AuthCredential = - AuthCredential(firebase.auth.GoogleAuthProvider.credential(idToken, accessToken)) + actual fun credential(idToken: String?, accessToken: String?): AuthCredential { + require(idToken != null || accessToken != null) { + "Both parameters are optional but at least one must be present." + } + return AuthCredential(firebase.auth.GoogleAuthProvider.credential(idToken, accessToken)) + } } actual class OAuthProvider(val js: firebase.auth.OAuthProvider) { @@ -81,4 +85,4 @@ actual interface PhoneVerificationProvider { actual object TwitterAuthProvider { actual fun credential(token: String, secret: String): AuthCredential = AuthCredential(firebase.auth.TwitterAuthProvider.credential(token, secret)) -} \ No newline at end of file +} diff --git a/firebase-auth/src/jsTest/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/jsTest/kotlin/dev/gitlive/firebase/auth/auth.kt index 93fb3effa..340f1fa6c 100644 --- a/firebase-auth/src/jsTest/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/jsTest/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -11,15 +11,8 @@ actual val emulatorHost: String = "localhost" actual val context: Any = Unit -actual val currentPlatform: Platform = Platform.JS - -actual fun runTest(skip: Boolean, test: suspend () -> Unit) = GlobalScope +actual fun runTest(test: suspend () -> Unit) = GlobalScope .promise { - if (skip) { - console.log("Skip the test.") - return@promise - } - try { test() } catch (e: dynamic) { diff --git a/firebase-auth/src/nativeInterop/cinterop/Cartfile b/firebase-auth/src/nativeInterop/cinterop/Cartfile index c87f8aa12..cd89fe4b6 100644 --- a/firebase-auth/src/nativeInterop/cinterop/Cartfile +++ b/firebase-auth/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json" == 8.5.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAuthBinary.json" == 8.9.1 diff --git a/firebase-common/build.gradle.kts b/firebase-common/build.gradle.kts index 3a2f2282d..44f7521e5 100644 --- a/firebase-common/build.gradle.kts +++ b/firebase-common/build.gradle.kts @@ -9,7 +9,7 @@ version = project.property("firebase-common.version") as String plugins { id("com.android.library") kotlin("multiplatform") - kotlin("plugin.serialization") version "1.5.21" + kotlin("plugin.serialization") version "1.5.31" } android { @@ -46,14 +46,11 @@ kotlin { publishAllLibraryVariants() } - fun nativeTargetConfig(): KotlinNativeTarget.() -> Unit = { + val supportIosTarget = project.property("skipIosTarget") != "true" - } - - if (project.extra["ideaActive"] as Boolean) { - iosX64("ios", nativeTargetConfig()) - } else { - ios(configure = nativeTargetConfig()) + if (supportIosTarget) { + ios() + iosSimulatorArm64() } js { @@ -80,35 +77,49 @@ kotlin { apiVersion = "1.5" languageVersion = "1.5" progressiveMode = true - useExperimentalAnnotation("kotlin.Experimental") - useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi") - useExperimentalAnnotation("kotlinx.serialization.ExperimentalSerializationApi") - useExperimentalAnnotation("kotlinx.serialization.InternalSerializationApi") + optIn("kotlin.Experimental") + optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") + optIn("kotlinx.serialization.ExperimentalSerializationApi") + optIn("kotlinx.serialization.InternalSerializationApi") } } val commonMain by getting { dependencies { - api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.2.2") + api("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.0") } } val androidMain by getting { dependencies { - api("com.google.firebase:firebase-common-ktx") + api("com.google.firebase:firebase-common") } } - val iosMain by getting + 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 { dependencies { - api(npm("firebase", "8.7.1")) + api(npm("firebase", "9.4.1")) } } } } +if (project.property("firebase-common.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 diff --git a/firebase-common/package.json b/firebase-common/package.json index 885ace498..306f5de28 100644 --- a/firebase-common/package.json +++ b/firebase-common/package.json @@ -23,9 +23,9 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-multiplatform-sdk", "dependencies": { - "firebase": "8.8.1", - "kotlin": "1.5.21", - "kotlinx-coroutines-core": "1.5.1", - "kotlinx-serialization-kotlinx-serialization-runtime": "1.2.2" + "firebase": "9.4.1", + "kotlin": "1.5.31", + "kotlinx-coroutines-core": "1.5.2", + "kotlinx-serialization-kotlinx-serialization-runtime": "1.3.0" } } diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/serializers.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/serializers.kt index 32ca42c38..43461fa15 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/serializers.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/serializers.kt @@ -36,7 +36,6 @@ class FirebaseMapSerializer : KSerializer> { override fun isElementOptional(index: Int) = false } - @InternalSerializationApi @Suppress("UNCHECKED_CAST") override fun serialize(encoder: Encoder, value: Map) { map = value @@ -81,7 +80,6 @@ class FirebaseListSerializer : KSerializer> { override fun isElementOptional(index: Int) = false } - @InternalSerializationApi @Suppress("UNCHECKED_CAST") override fun serialize(encoder: Encoder, value: Iterable) { list = value.toList() diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt index 3a058e68b..0da033158 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt @@ -2,7 +2,7 @@ * Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license. */ -@file:JsModule("firebase/app") +@file:JsModule("firebase/compat/app") package dev.gitlive.firebase @@ -57,12 +57,14 @@ external object firebase { fun fetchSignInMethodsForEmail(email: String): Promise> fun sendPasswordResetEmail(email: String, actionCodeSettings: Any?): Promise fun sendSignInLinkToEmail(email: String, actionCodeSettings: Any?): Promise + fun isSignInWithEmailLink(link: String): Boolean fun signInWithEmailAndPassword(email: String, password: String): Promise fun signInWithCustomToken(token: String): Promise fun signInAnonymously(): Promise fun signInWithCredential(authCredential: AuthCredential): Promise fun signInWithPopup(provider: AuthProvider): Promise fun signInWithRedirect(provider: AuthProvider): Promise + fun signInWithEmailLink(email: String, link: String): Promise fun getRedirectResult(): Promise fun signOut(): Promise fun updateCurrentUser(user: user.User?): Promise diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals2.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals2.kt index fbc2409a8..3c2b981c1 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals2.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals2.kt @@ -6,19 +6,24 @@ package dev.gitlive.firebase import kotlin.js.Promise -@JsModule("firebase/functions") +@JsModule("firebase/compat/functions") +@JsName("default") external object functions -@JsModule("firebase/auth") +@JsModule("firebase/compat/auth") +@JsName("default") external object auth -@JsModule("firebase/database") +@JsModule("firebase/compat/database") +@JsName("default") external object database -@JsModule("firebase/firestore") +@JsModule("firebase/compat/firestore") +@JsName("default") external object firestore -@JsModule("firebase/remote-config") +@JsModule("firebase/compat/remote-config") +@JsName("default") external object remoteConfig typealias SnapshotCallback = (data: firebase.database.DataSnapshot, b: String?) -> Unit diff --git a/firebase-config/build.gradle.kts b/firebase-config/build.gradle.kts index 117069ab5..6b113bdcd 100644 --- a/firebase-config/build.gradle.kts +++ b/firebase-config/build.gradle.kts @@ -3,6 +3,7 @@ */ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.konan.target.KonanTarget version = project.property("firebase-config.version") as String @@ -55,57 +56,61 @@ android { // logEmulatorOutput(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() } - fun nativeTargetConfig(): KotlinNativeTarget.() -> Unit = { - val nativeFrameworkPaths = listOf( - "FirebaseCore", - "FirebaseCoreDiagnostics", - "FirebaseAnalytics", - "GoogleAppMeasurement", - "FirebaseInstallations", - "GoogleDataTransport", - "GoogleUtilities", - "PromisesObjC", - "nanopb" - ).map { - val archVariant = - if (konanTarget is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - rootProject.project("firebase-app").projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - }.plus( - listOf( - "FirebaseABTesting", - "FirebaseRemoteConfig" + val supportIosTarget = project.property("skipIosTarget") != "true" + if (supportIosTarget) { + fun nativeTargetConfig(): KotlinNativeTarget.() -> Unit = { + val nativeFrameworkPaths = listOf( + "FirebaseCore", + "FirebaseCoreDiagnostics", + "FirebaseAnalytics", + "GoogleAppMeasurement", + "GoogleAppMeasurementIdentitySupport", + "FirebaseInstallations", + "GoogleDataTransport", + "GoogleUtilities", + "PromisesObjC", + "nanopb" ).map { - val archVariant = - if (konanTarget is org.jetbrains.kotlin.konan.target.KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ) + rootProject.project("firebase-app").projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/${konanTarget.archVariant}") + }.plus( + listOf( + "FirebaseABTesting", + "FirebaseRemoteConfig" + ).map { + projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/${konanTarget.archVariant}") + } + ) - binaries { - getTest("DEBUG").apply { - linkerOpts(nativeFrameworkPaths.map { "-F$it" }) - linkerOpts("-ObjC") + binaries { + getTest("DEBUG").apply { + linkerOpts(nativeFrameworkPaths.map { "-F$it" }) + linkerOpts("-ObjC") + } } - } - compilations.getByName("main") { - cinterops.create("FirebaseRemoteConfig") { - compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + compilations.getByName("main") { + cinterops.create("FirebaseRemoteConfig") { + compilerOpts(nativeFrameworkPaths.map { "-F$it" }) + extraOpts = listOf("-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=", "-verbose") + } } } - } - if (project.extra["ideaActive"] as Boolean) { - iosX64("ios", nativeTargetConfig()) - } else { ios(configure = nativeTargetConfig()) + iosSimulatorArm64(configure = nativeTargetConfig()) } js { @@ -125,7 +130,7 @@ kotlin { apiVersion = "1.5" languageVersion = "1.5" progressiveMode = true - useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi") + optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") } } @@ -138,16 +143,30 @@ kotlin { val androidMain by getting { dependencies { - api("com.google.firebase:firebase-config-ktx") + api("com.google.firebase:firebase-config") } } - val iosMain by getting + 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-config.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 diff --git a/firebase-config/package.json b/firebase-config/package.json index ed179cfcb..9a2b6103c 100644 --- a/firebase-config/package.json +++ b/firebase-config/package.json @@ -23,9 +23,9 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "1.4.1", - "firebase": "8.8.1", - "kotlin": "1.5.21", - "kotlinx-coroutines-core": "1.5.1" + "@gitlive/firebase-app": "1.4.4", + "firebase": "9.4.1", + "kotlin": "1.5.31", + "kotlinx-coroutines-core": "1.5.2" } } diff --git a/firebase-config/src/androidMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt b/firebase-config/src/androidMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt index 59b9108b7..2e43b9a10 100644 --- a/firebase-config/src/androidMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt +++ b/firebase-config/src/androidMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt @@ -4,21 +4,18 @@ package dev.gitlive.firebase.remoteconfig import com.google.firebase.remoteconfig.FirebaseRemoteConfigClientException import com.google.firebase.remoteconfig.FirebaseRemoteConfigFetchThrottledException import com.google.firebase.remoteconfig.FirebaseRemoteConfigServerException -import com.google.firebase.remoteconfig.ktx.remoteConfig -import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.FirebaseApp import kotlinx.coroutines.tasks.await -import com.google.firebase.ktx.Firebase as AndroidFirebase import com.google.firebase.remoteconfig.FirebaseRemoteConfig as AndroidFirebaseRemoteConfig import com.google.firebase.remoteconfig.FirebaseRemoteConfigInfo as AndroidFirebaseRemoteConfigInfo import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings as AndroidFirebaseRemoteConfigSettings actual val Firebase.remoteConfig: FirebaseRemoteConfig - get() = FirebaseRemoteConfig(AndroidFirebase.remoteConfig) + get() = FirebaseRemoteConfig(com.google.firebase.remoteconfig.FirebaseRemoteConfig.getInstance()) actual fun Firebase.remoteConfig(app: FirebaseApp): FirebaseRemoteConfig = - FirebaseRemoteConfig(AndroidFirebase.remoteConfig) + FirebaseRemoteConfig(com.google.firebase.remoteconfig.FirebaseRemoteConfig.getInstance(app.android)) actual class FirebaseRemoteConfig internal constructor(val android: AndroidFirebaseRemoteConfig) { actual val all: Map @@ -29,10 +26,10 @@ actual class FirebaseRemoteConfig internal constructor(val android: AndroidFireb actual suspend fun settings(init: FirebaseRemoteConfigSettings.() -> Unit) { val settings = FirebaseRemoteConfigSettings().apply(init) - val androidSettings = remoteConfigSettings { - minimumFetchIntervalInSeconds = settings.minimumFetchIntervalInSeconds - fetchTimeoutInSeconds = settings.fetchTimeoutInSeconds - } + val androidSettings = com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings.Builder() + .setMinimumFetchIntervalInSeconds(settings.minimumFetchIntervalInSeconds) + .setFetchTimeoutInSeconds(settings.fetchTimeoutInSeconds) + .build() android.setConfigSettingsAsync(androidSettings).await() } diff --git a/firebase-config/src/nativeInterop/cinterop/Cartfile b/firebase-config/src/nativeInterop/cinterop/Cartfile index 525ebedd6..031cc8495 100644 --- a/firebase-config/src/nativeInterop/cinterop/Cartfile +++ b/firebase-config/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" == 8.5.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseRemoteConfigBinary.json" == 8.9.1 diff --git a/firebase-config/src/nativeInterop/cinterop/FirebaseRemoteConfig.def b/firebase-config/src/nativeInterop/cinterop/FirebaseRemoteConfig.def index 0d79ca265..366525c26 100644 --- a/firebase-config/src/nativeInterop/cinterop/FirebaseRemoteConfig.def +++ b/firebase-config/src/nativeInterop/cinterop/FirebaseRemoteConfig.def @@ -2,4 +2,4 @@ language = Objective-C package = cocoapods.FirebaseRemoteConfig modules = FirebaseRemoteConfig compilerOpts = -framework FirebaseRemoteConfig -linkerOpts = -framework FirebaseRemoteConfig -framework FirebaseABTesting +linkerOpts = -framework FirebaseABTesting -framework FirebaseRemoteConfig diff --git a/firebase-database/build.gradle.kts b/firebase-database/build.gradle.kts index 91646f312..d96ab2e07 100644 --- a/firebase-database/build.gradle.kts +++ b/firebase-database/build.gradle.kts @@ -10,7 +10,8 @@ version = project.property("firebase-database.version") as String plugins { id("com.android.library") kotlin("multiplatform") - kotlin("plugin.serialization") version "1.5.10" + kotlin("plugin.serialization") version "1.5.31" + kotlin("native.cocoapods") } repositories { @@ -44,61 +45,65 @@ android { } } +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() } - 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", - "GoogleDataTransport", - "GoogleUtilities", - "nanopb", - "PromisesObjC" - ).map { - val archVariant = if (konanTarget is KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - - rootProject.project("firebase-app").projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ).plus( - listOf( - "FirebaseDatabase", - "leveldb-library" - ).map { - val archVariant = if (konanTarget is KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - - projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ) + 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}") + } + ).plus( + listOf( + "FirebaseDatabase", + "leveldb-library" + ).map { + projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/${konanTarget.archVariant}") + } + ) - binaries { - getTest("DEBUG").apply { - linkerOpts(nativeFrameworkPaths.map { "-F$it" }) - linkerOpts("-ObjC") + binaries { + getTest("DEBUG").apply { + linkerOpts(nativeFrameworkPaths.map { "-F$it" }) + linkerOpts("-ObjC") + } } - } - compilations.getByName("main") { - cinterops.create("FirebaseDatabase") { - compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + compilations.getByName("main") { + cinterops.create("FirebaseDatabase") { + compilerOpts(nativeFrameworkPaths.map { "-F$it" }) + extraOpts = listOf("-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=", "-verbose") + } } } - } - if (project.extra["ideaActive"] as Boolean) { - iosX64("ios", nativeTargetConfig()) - } else { ios(configure = nativeTargetConfig()) + iosSimulatorArm64(configure = nativeTargetConfig()) } js { @@ -125,9 +130,9 @@ kotlin { apiVersion = "1.5" languageVersion = "1.5" progressiveMode = true - useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi") - useExperimentalAnnotation("kotlinx.coroutines.FlowPreview") - useExperimentalAnnotation("kotlinx.serialization.InternalSerializationApi") + optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") + optIn("kotlinx.coroutines.FlowPreview") + optIn("kotlinx.serialization.InternalSerializationApi") } } @@ -140,16 +145,30 @@ kotlin { val androidMain by getting { dependencies { - api("com.google.firebase:firebase-database-ktx") + api("com.google.firebase:firebase-database") } } - val iosMain by getting + 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-database.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 diff --git a/firebase-database/package.json b/firebase-database/package.json index c2eedf3cd..d554e3b67 100644 --- a/firebase-database/package.json +++ b/firebase-database/package.json @@ -23,9 +23,9 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "1.4.1", - "firebase": "8.8.1", - "kotlin": "1.5.21", - "kotlinx-coroutines-core": "1.5.1" + "@gitlive/firebase-app": "1.4.4", + "firebase": "9.4.1", + "kotlin": "1.5.31", + "kotlinx-coroutines-core": "1.5.2" } } diff --git a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt index 06f4e8ad0..1eb5a5ac0 100644 --- a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -66,6 +66,9 @@ actual class FirebaseDatabase internal constructor(val android: com.google.fireb actual fun reference(path: String) = DatabaseReference(android.getReference(path), persistenceEnabled) + actual fun reference() = + DatabaseReference(android.reference, persistenceEnabled) + actual fun setPersistenceEnabled(enabled: Boolean) = android.setPersistenceEnabled(enabled).also { persistenceEnabled = enabled } @@ -192,8 +195,12 @@ actual class DatabaseReference internal constructor( val deferred = CompletableDeferred() android.runTransaction(object : Transaction.Handler { - override fun doTransaction(currentData: MutableData) = - Transaction.success(transactionUpdate(decode(strategy, currentData)) as MutableData) + override fun doTransaction(currentData: MutableData): Transaction.Result { + currentData.value = currentData.value?.let { + transactionUpdate(decode(strategy, it)) + } + return Transaction.success(currentData) + } override fun onComplete( error: DatabaseError?, diff --git a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt index afec518e9..0936c1123 100644 --- a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -26,6 +26,7 @@ expect fun Firebase.database(app: FirebaseApp, url: String): FirebaseDatabase expect class FirebaseDatabase { fun reference(path: String): DatabaseReference + fun reference(): DatabaseReference fun setPersistenceEnabled(enabled: Boolean) fun setLoggingEnabled(enabled: Boolean) fun useEmulator(host: String, port: Int) diff --git a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt index c7c68745c..08adf3b9b 100644 --- a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -52,6 +52,9 @@ actual class FirebaseDatabase internal constructor(val ios: FIRDatabase) { actual fun reference(path: String) = DatabaseReference(ios.referenceWithPath(path), ios.persistenceEnabled) + actual fun reference() = + DatabaseReference(ios.reference(), ios.persistenceEnabled) + actual fun setPersistenceEnabled(enabled: Boolean) { ios.persistenceEnabled = enabled } diff --git a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt index bb1dd4b46..b560102b2 100644 --- a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -38,6 +38,7 @@ actual fun Firebase.database(app: FirebaseApp, url: String) = actual class FirebaseDatabase internal constructor(val js: firebase.database.Database) { actual fun reference(path: String) = rethrow { DatabaseReference(js.ref(path)) } + actual fun reference() = rethrow { DatabaseReference(js.ref()) } actual fun setPersistenceEnabled(enabled: Boolean) {} actual fun setLoggingEnabled(enabled: Boolean) = rethrow { firebase.database.enableLogging(enabled) } actual fun useEmulator(host: String, port: Int) = rethrow { js.useEmulator(host, port) } diff --git a/firebase-database/src/nativeInterop/cinterop/Cartfile b/firebase-database/src/nativeInterop/cinterop/Cartfile index 3391e187f..47d3f6e64 100644 --- a/firebase-database/src/nativeInterop/cinterop/Cartfile +++ b/firebase-database/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDatabaseBinary.json" == 8.5.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseDatabaseBinary.json" == 8.9.1 diff --git a/firebase-firestore/build.gradle.kts b/firebase-firestore/build.gradle.kts index bafa41f76..dbd1d798f 100644 --- a/firebase-firestore/build.gradle.kts +++ b/firebase-firestore/build.gradle.kts @@ -10,7 +10,7 @@ version = project.property("firebase-firestore.version") as String plugins { id("com.android.library") kotlin("multiplatform") - kotlin("plugin.serialization") version "1.5.21" + kotlin("plugin.serialization") version "1.5.31" } android { @@ -46,65 +46,69 @@ android { } } +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() } - 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", - "GoogleDataTransport", - "GoogleUtilities", - "nanopb", - "PromisesObjC" - ).map { - val archVariant = if (konanTarget is KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - - rootProject.project("firebase-app").projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ).plus( - listOf( - "abseil", - "BoringSSL-GRPC", - "FirebaseFirestore", - "gRPC-Core", - "gRPC-C++", - "leveldb-library" - ).map { - val archVariant = if (konanTarget is KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - - projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ) + 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}") + } + ).plus( + listOf( + "abseil", + "BoringSSL-GRPC", + "FirebaseFirestore", + "gRPC-C++", + "gRPC-Core", + "leveldb-library" + ).map { + projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/${konanTarget.archVariant}") + } + ) - binaries { - getTest("DEBUG").apply { - linkerOpts(nativeFrameworkPaths.map { "-F$it" }) - linkerOpts("-ObjC") + binaries { + getTest("DEBUG").apply { + linkerOpts(nativeFrameworkPaths.map { "-F$it" }) + linkerOpts("-ObjC") + } } - } - compilations.getByName("main") { - cinterops.create("FirebaseFirestore") { - compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + compilations.getByName("main") { + cinterops.create("FirebaseFirestore") { + compilerOpts(nativeFrameworkPaths.map { "-F$it" }) + extraOpts = listOf("-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=", "-verbose") + } } } - } - if (project.extra["ideaActive"] as Boolean) { - iosX64("ios", nativeTargetConfig()) - } else { ios(configure = nativeTargetConfig()) + iosSimulatorArm64(configure = nativeTargetConfig()) } js { @@ -131,8 +135,8 @@ kotlin { apiVersion = "1.5" languageVersion = "1.5" progressiveMode = true - useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi") - useExperimentalAnnotation("kotlinx.serialization.InternalSerializationApi") + optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") + optIn("kotlinx.serialization.InternalSerializationApi") } } @@ -145,15 +149,30 @@ kotlin { val androidMain by getting { dependencies { - api("com.google.firebase:firebase-firestore-ktx") + api("com.google.firebase:firebase-firestore") } } - val iosMain by getting + 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-firestore.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 diff --git a/firebase-firestore/package.json b/firebase-firestore/package.json index b06b89a2b..5e2a4b8b8 100644 --- a/firebase-firestore/package.json +++ b/firebase-firestore/package.json @@ -23,9 +23,9 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "1.4.1", - "firebase": "8.8.1", - "kotlin": "1.5.21", - "kotlinx-coroutines-core": "1.5.1" + "@gitlive/firebase-app": "1.4.4", + "firebase": "9.4.1", + "kotlin": "1.5.31", + "kotlinx-coroutines-core": "1.5.2" } } diff --git a/firebase-firestore/src/androidAndroidTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidAndroidTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 34a69d9fd..9c3f485e2 100644 --- a/firebase-firestore/src/androidAndroidTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidAndroidTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -6,10 +6,11 @@ package dev.gitlive.firebase.firestore import androidx.test.platform.app.InstrumentationRegistry +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.runBlocking actual val emulatorHost: String = "10.0.2.2" actual val context: Any = InstrumentationRegistry.getInstrumentation().targetContext -actual fun runTest(test: suspend () -> Unit) = runBlocking { test() } +actual fun runTest(test: suspend CoroutineScope.() -> Unit) = runBlocking { test() } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index cffad477a..115d4a114 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -409,20 +409,29 @@ actual class DocumentSnapshot(val android: com.google.firebase.firestore.Documen actual val id get() = android.id actual val reference get() = DocumentReference(android.reference) - actual inline fun data() = decode(value = android.data) + actual inline fun data(serverTimestampBehavior: ServerTimestampBehavior): T = + decode(value = android.getData(serverTimestampBehavior.toAndroid())) - actual fun data(strategy: DeserializationStrategy) = decode(strategy, android.data) + actual fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior): T = + decode(strategy, android.getData(serverTimestampBehavior.toAndroid())) - actual inline fun get(field: String) = decode(value = android.get(field)) + actual inline fun get(field: String, serverTimestampBehavior: ServerTimestampBehavior): T = + decode(value = android.get(field, serverTimestampBehavior.toAndroid())) - actual fun get(field: String, strategy: DeserializationStrategy) = - decode(strategy, android.get(field)) + actual fun get(field: String, strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior): T = + decode(strategy, android.get(field, serverTimestampBehavior.toAndroid())) actual fun contains(field: String) = android.contains(field) actual val exists get() = android.exists() actual val metadata: SnapshotMetadata get() = SnapshotMetadata(android.metadata) + + fun ServerTimestampBehavior.toAndroid(): com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior = when (this) { + ServerTimestampBehavior.ESTIMATE -> com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior.ESTIMATE + ServerTimestampBehavior.NONE -> com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior.NONE + ServerTimestampBehavior.PREVIOUS -> com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior.PREVIOUS + } } actual class SnapshotMetadata(val android: com.google.firebase.firestore.SnapshotMetadata) { @@ -442,4 +451,3 @@ actual object FieldValue { actual fun arrayRemove(vararg elements: Any): Any = FieldValue.arrayRemove(*elements) actual fun delete(): Any = delete } - diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 2e5409070..366b416b2 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -188,13 +188,13 @@ expect class DocumentChange { expect class DocumentSnapshot { - inline fun get(field: String): T - fun get(field: String, strategy: DeserializationStrategy): T + inline fun get(field: String, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T + fun get(field: String, strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T fun contains(field: String): Boolean - inline fun data(): T - fun data(strategy: DeserializationStrategy): T + inline fun data(serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T + fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior = ServerTimestampBehavior.NONE): T val exists: Boolean val id: String @@ -202,6 +202,12 @@ expect class DocumentSnapshot { val metadata: SnapshotMetadata } +enum class ServerTimestampBehavior { + ESTIMATE, + NONE, + PREVIOUS +} + expect class SnapshotMetadata { val hasPendingWrites: Boolean val isFromCache: Boolean diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 7c6e64e23..f48ae582d 100644 --- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -4,13 +4,29 @@ package dev.gitlive.firebase.firestore -import dev.gitlive.firebase.* -import kotlinx.serialization.* -import kotlin.test.* +import dev.gitlive.firebase.Firebase +import dev.gitlive.firebase.FirebaseOptions +import dev.gitlive.firebase.apps +import dev.gitlive.firebase.initialize +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withTimeout +import kotlinx.serialization.Serializable +import kotlin.random.Random +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue expect val emulatorHost: String expect val context: Any -expect fun runTest(test: suspend () -> Unit) +expect fun runTest(test: suspend CoroutineScope.() -> Unit) class FirebaseFirestoreTest { @@ -121,9 +137,71 @@ class FirebaseFirestoreTest { assertNotEquals(FieldValue.serverTimestamp, doc.get().get("time")) assertNotEquals(FieldValue.serverTimestamp, doc.get().data(FirestoreTest.serializer()).time) + } + + @Test + fun testServerTimestampBehaviorNone() = runTest { + val doc = Firebase.firestore + .collection("testServerTimestampBehaviorNone") + .document("test${Random.nextInt()}") + + val deferredPendingWritesSnapshot = async { + withTimeout(5000) { + doc.snapshots.filter { it.exists }.first() + } + } + delay(100) // makes possible to catch pending writes snapshot + + doc.set( + FirestoreTest.serializer(), + FirestoreTest("ServerTimestampBehavior", FieldValue.serverTimestamp) + ) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNull(pendingWritesSnapshot.get("time", ServerTimestampBehavior.NONE)) + } + + @Test + fun testServerTimestampBehaviorEstimate() = runTest { + val doc = Firebase.firestore + .collection("testServerTimestampBehaviorEstimate") + .document("test${Random.nextInt()}") + + val deferredPendingWritesSnapshot = async { + withTimeout(5000) { + doc.snapshots.filter { it.exists }.first() + } + } + delay(100) // makes possible to catch pending writes snapshot + doc.set(FirestoreTest.serializer(), FirestoreTest("ServerTimestampBehavior", FieldValue.serverTimestamp)) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNotNull(pendingWritesSnapshot.get("time", ServerTimestampBehavior.ESTIMATE)) + assertNotEquals(0.0, pendingWritesSnapshot.data(FirestoreTest.serializer(), ServerTimestampBehavior.ESTIMATE).time) } + @Test + fun testServerTimestampBehaviorPrevious() = runTest { + val doc = Firebase.firestore + .collection("testServerTimestampBehaviorPrevious") + .document("test${Random.nextInt()}") + + val deferredPendingWritesSnapshot = async { + withTimeout(5000) { + doc.snapshots.filter { it.exists }.first() + } + } + delay(100) // makes possible to catch pending writes snapshot + + doc.set(FirestoreTest.serializer(), FirestoreTest("ServerTimestampBehavior", FieldValue.serverTimestamp)) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNull(pendingWritesSnapshot.get("time", ServerTimestampBehavior.PREVIOUS)) + } @Test fun testDocumentAutoId() = runTest { @@ -153,4 +231,4 @@ class FirebaseFirestoreTest { .document("three") .set(FirestoreTest.serializer(), FirestoreTest("ccc")) } -} \ No newline at end of file +} diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 015777452..02dfb91c5 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy import platform.Foundation.NSError +import platform.Foundation.NSNull @PublishedApi internal inline fun decode(value: Any?): T = @@ -377,20 +378,37 @@ actual class DocumentSnapshot(val ios: FIRDocumentSnapshot) { actual val reference get() = DocumentReference(ios.reference) - actual inline fun data() = decode(value = ios.data()) + actual inline fun data(serverTimestampBehavior: ServerTimestampBehavior): T { + val data = ios.dataWithServerTimestampBehavior(serverTimestampBehavior.toIos()) + return decode(value = data?.mapValues { (_, value) -> value?.takeIf { it !is NSNull } }) + } - actual fun data(strategy: DeserializationStrategy) = decode(strategy, ios.data()) + actual fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior): T { + val data = ios.dataWithServerTimestampBehavior(serverTimestampBehavior.toIos()) + return decode(strategy, data?.mapValues { (_, value) -> value?.takeIf { it !is NSNull } }) + } - actual inline fun get(field: String) = decode(value = ios.valueForField(field)) + actual inline fun get(field: String, serverTimestampBehavior: ServerTimestampBehavior): T { + val value = ios.valueForField(field, serverTimestampBehavior.toIos())?.takeIf { it !is NSNull } + return decode(value) + } - actual fun get(field: String, strategy: DeserializationStrategy) = - decode(strategy, ios.valueForField(field)) + actual fun get(field: String, strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior): T { + val value = ios.valueForField(field, serverTimestampBehavior.toIos())?.takeIf { it !is NSNull } + return decode(strategy, value) + } actual fun contains(field: String) = ios.valueForField(field) != null actual val exists get() = ios.exists actual val metadata: SnapshotMetadata get() = SnapshotMetadata(ios.metadata) + + fun ServerTimestampBehavior.toIos() : FIRServerTimestampBehavior = when (this) { + ServerTimestampBehavior.ESTIMATE -> FIRServerTimestampBehavior.FIRServerTimestampBehaviorEstimate + ServerTimestampBehavior.NONE -> FIRServerTimestampBehavior.FIRServerTimestampBehaviorNone + ServerTimestampBehavior.PREVIOUS -> FIRServerTimestampBehavior.FIRServerTimestampBehaviorPrevious + } } actual class SnapshotMetadata(val ios: FIRSnapshotMetadata) { diff --git a/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt index d4915f9d4..294182dd3 100644 --- a/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -11,7 +11,7 @@ actual val emulatorHost: String = "localhost" actual val context: Any = Unit -actual fun runTest(test: suspend () -> Unit) = runBlocking { +actual fun runTest(test: suspend CoroutineScope.() -> Unit) = runBlocking { val testRun = MainScope().async { test() } while (testRun.isActive) { NSRunLoop.mainRunLoop.runMode( diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index b72393afd..fadb1a6b2 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -387,21 +387,24 @@ actual class DocumentSnapshot(val js: firebase.firestore.DocumentSnapshot) { actual val id get() = rethrow { js.id } actual val reference get() = rethrow { DocumentReference(js.ref) } - actual inline fun data(): T = - rethrow { decode(value = js.data()) } + actual inline fun data(serverTimestampBehavior: ServerTimestampBehavior): T = + rethrow { decode(value = js.data(getTimestampsOptions(serverTimestampBehavior))) } - actual fun data(strategy: DeserializationStrategy): T = - rethrow { decode(strategy, js.data()) } + actual fun data(strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior): T = + rethrow { decode(strategy, js.data(getTimestampsOptions(serverTimestampBehavior))) } - actual inline fun get(field: String) = - rethrow { decode(value = js.get(field)) } + actual inline fun get(field: String, serverTimestampBehavior: ServerTimestampBehavior) = + rethrow { decode(value = js.get(field, getTimestampsOptions(serverTimestampBehavior))) } - actual fun get(field: String, strategy: DeserializationStrategy) = - rethrow { decode(strategy, js.get(field)) } + actual fun get(field: String, strategy: DeserializationStrategy, serverTimestampBehavior: ServerTimestampBehavior) = + rethrow { decode(strategy, js.get(field, getTimestampsOptions(serverTimestampBehavior))) } actual fun contains(field: String) = rethrow { js.get(field) != undefined } actual val exists get() = rethrow { js.exists } actual val metadata: SnapshotMetadata get() = SnapshotMetadata(js.metadata) + + fun getTimestampsOptions(serverTimestampBehavior: ServerTimestampBehavior) = + json("serverTimestamps" to serverTimestampBehavior.name.lowercase()) } actual class SnapshotMetadata(val js: firebase.firestore.SnapshotMetadata) { @@ -503,3 +506,13 @@ fun errorToException(e: dynamic) = (e?.code ?: e?.message ?: "") } } } + +// from: https://discuss.kotlinlang.org/t/how-to-access-native-js-object-as-a-map-string-any/509/8 +fun entriesOf(jsObject: dynamic): List> = + (js("Object.entries") as (dynamic) -> Array>) + .invoke(jsObject) + .map { entry -> entry[0] as String to entry[1] } + +// from: https://discuss.kotlinlang.org/t/how-to-access-native-js-object-as-a-map-string-any/509/8 +fun mapOf(jsObject: dynamic): Map = + entriesOf(jsObject).toMap() diff --git a/firebase-firestore/src/jsTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 0987555ca..c4b272bb8 100644 --- a/firebase-firestore/src/jsTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -4,6 +4,7 @@ package dev.gitlive.firebase.firestore +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.promise @@ -11,12 +12,12 @@ actual val emulatorHost: String = "localhost" actual val context: Any = Unit -actual fun runTest(test: suspend () -> Unit) = GlobalScope +actual fun runTest(test: suspend CoroutineScope.() -> Unit) = GlobalScope .promise { try { test() } catch (e: dynamic) { - e.log() + (e as? Throwable)?.log() throw e } }.asDynamic() @@ -28,4 +29,3 @@ internal fun Throwable.log() { it.log() } } - diff --git a/firebase-firestore/src/nativeInterop/cinterop/Cartfile b/firebase-firestore/src/nativeInterop/cinterop/Cartfile index f8ef0e3ab..3db33c26e 100644 --- a/firebase-firestore/src/nativeInterop/cinterop/Cartfile +++ b/firebase-firestore/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFirestoreBinary.json" == 8.5.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFirestoreBinary.json" == 8.9.1 diff --git a/firebase-functions/build.gradle.kts b/firebase-functions/build.gradle.kts index 5d4f7454a..5a644cfb2 100644 --- a/firebase-functions/build.gradle.kts +++ b/firebase-functions/build.gradle.kts @@ -38,61 +38,65 @@ android { } } +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() } - 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", - "GoogleDataTransport", - "GoogleUtilities", - "nanopb", - "PromisesObjC" - ).map { - val archVariant = if (konanTarget is KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - - rootProject.project("firebase-app").projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ).plus( - listOf( - "FirebaseFunctions", - "GTMSessionFetcher" - ).map { - val archVariant = if (konanTarget is KonanTarget.IOS_X64) "ios-arm64_i386_x86_64-simulator" else "ios-arm64_armv7" - - projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/$archVariant") - } - ) + 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}") + } + ).plus( + listOf( + "FirebaseFunctions", + "GTMSessionFetcher" + ).map { + projectDir.resolve("src/nativeInterop/cinterop/Carthage/Build/$it.xcframework/${konanTarget.archVariant}") + } + ) - binaries { - getTest("DEBUG").apply { - linkerOpts(nativeFrameworkPaths.map { "-F$it" }) - linkerOpts("-ObjC") + binaries { + getTest("DEBUG").apply { + linkerOpts(nativeFrameworkPaths.map { "-F$it" }) + linkerOpts("-ObjC") + } } - } - compilations.getByName("main") { - cinterops.create("FirebaseFunctions") { - compilerOpts(nativeFrameworkPaths.map { "-F$it" }) - extraOpts("-verbose") + compilations.getByName("main") { + cinterops.create("FirebaseFunctions") { + compilerOpts(nativeFrameworkPaths.map { "-F$it" }) + extraOpts = listOf("-compiler-option", "-DNS_FORMAT_ARGUMENT(A)=", "-verbose") + } } } - } - if (project.extra["ideaActive"] as Boolean) { - iosX64("ios", nativeTargetConfig()) - } else { ios(configure = nativeTargetConfig()) + iosSimulatorArm64(configure = nativeTargetConfig()) } js { @@ -119,8 +123,8 @@ kotlin { apiVersion = "1.5" languageVersion = "1.5" progressiveMode = true - useExperimentalAnnotation("kotlinx.coroutines.ExperimentalCoroutinesApi") - useExperimentalAnnotation("kotlinx.serialization.InternalSerializationApi") + optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") + optIn("kotlinx.serialization.InternalSerializationApi") } } @@ -133,16 +137,30 @@ kotlin { val androidMain by getting { dependencies { - api("com.google.firebase:firebase-functions-ktx") + api("com.google.firebase:firebase-functions") } } - val iosMain by getting + 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-functions.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 diff --git a/firebase-functions/package.json b/firebase-functions/package.json index a2e96aec7..391121397 100644 --- a/firebase-functions/package.json +++ b/firebase-functions/package.json @@ -23,9 +23,9 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "1.4.1", - "firebase": "8.8.1", - "kotlin": "1.5.21", - "kotlinx-coroutines-core": "1.5.1" + "@gitlive/firebase-app": "1.4.4", + "firebase": "9.4.1", + "kotlin": "1.5.31", + "kotlinx-coroutines-core": "1.5.2" } } diff --git a/firebase-functions/src/nativeInterop/cinterop/Cartfile b/firebase-functions/src/nativeInterop/cinterop/Cartfile index 75898582b..c03a8d5ed 100644 --- a/firebase-functions/src/nativeInterop/cinterop/Cartfile +++ b/firebase-functions/src/nativeInterop/cinterop/Cartfile @@ -1 +1 @@ -binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFunctionsBinary.json" == 8.5.0 +binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseFunctionsBinary.json" == 8.9.1 diff --git a/gradle.properties b/gradle.properties index a0ae5bd10..3dd4326a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,20 +8,32 @@ kotlin.js.experimental.generateKotlinExternals=false #kotlin.mpp.enableCompatibilityMetadataVariant=true #kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.mpp.stability.nowarn=true -#kotlin.native.cacheKind=none +kotlin.native.cacheKind.iosX64=none #kotlin.native.enableDependencyPropagation=false kotlin.native.enableParallelExecutionCheck=false kotlin.setJvmTargetFromAndroidCompileOptions=true org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true -testOptions.unitTests.isIncludeAndroidResources = true -kotlin.native.cacheKind.iosX64=none +systemProp.org.gradle.internal.publish.checksums.insecure=true +testOptions.unitTests.isIncludeAndroidResources=true + +# Set to true to skip tests and even compilation of the iOS target. +skipIosTarget=false +# Skip iOS Tests +firebase-app.skipIosTests=false +# We are skipping auth ios tests due to an issue with keychain and simulator. +firebase-auth.skipIosTests=true +firebase-common.skipIosTests=false +firebase-database.skipIosTests=false +firebase-firestore.skipIosTests=false +firebase-functions.skipIosTests=false +firebase-config.skipIosTests=false # Versions: -firebase-app.version=1.4.1 -firebase-auth.version=1.4.1 -firebase-common.version=1.4.1 -firebase-database.version=1.4.1 -firebase-firestore.version=1.4.1 -firebase-functions.version=1.4.1 -firebase-config.version=1.4.1 +firebase-app.version=1.4.4 +firebase-auth.version=1.4.4 +firebase-common.version=1.4.4 +firebase-database.version=1.4.4 +firebase-firestore.version=1.4.4 +firebase-functions.version=1.4.4 +firebase-config.version=1.4.4 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 87b738cbd..7454180f2 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 469b8ac11..ffed3a254 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Wed May 20 18:31:34 BST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/gradlew b/gradlew index af6708ff2..1b6c78733 100755 --- a/gradlew +++ b/gradlew @@ -1,78 +1,129 @@ -#!/usr/bin/env sh +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -89,84 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 6d57edc70..107acd32c 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell