GitLiveApp / firebase-kotlin-sdk

A Kotlin-first SDK for Firebase
https://gitliveapp.github.io/firebase-kotlin-sdk/
Apache License 2.0
1.07k stars 153 forks source link

SDK Setup Instructions #288

Closed gchristov closed 2 years ago

gchristov commented 2 years ago

I have managed to setup my multiplatform project with this library for Android with no issues and it works great so far! However, iOS is giving me the following error when I try to build the app

ld: framework not found FirebaseFirestore

which I believe is similar to some of the other issues I've seen in this repo.

Would it be possible to include working setup instructions for iOS?

My setup for Android is exactly the same as the usual Firebase guide so I imagine iOS would be the same but I'm probably missing an important step somewhere in between. I should also mention that I have a multi-module project and Firebase is setup as a standalone module to be linked to the rest of the app.

Thanks very much for building the SDK and I'm hoping to have iOS working the same way as Android 🙏

suntrix commented 2 years ago

You still need to follow the normal steps to add Firebase iOS SDK to your iOS app (https://firebase.google.com/docs/ios/setup).

After you do that you shouldn't be seeing any linking errors.

gchristov commented 2 years ago

@suntrix I was following the Swift Package Manager instructions to add Firebase but I was still getting linker errors when executing embedAndSignAppleFrameworkForXcode with the above error.

What seems to work was to add all Firebase frameworks manually by downloading the bundle and doing this:

val xcf = XCFramework()
val libs = listOf(
    "FirebaseFirestore.xcframework",
    ...
)
val nativeFrameworkPaths =
        projectDir.resolve("PATH/TO/FIREBASE/DOWNLOAD").listFiles().filter { it.isDirectory }
            .flatMap { it.listFiles().filter { it.isDirectory && libs.contains(it.name) } }
iosTarget("ios") {
    binaries {
        framework {
            baseName = "KmmShared"
            xcf.add(this)
            val libraryIdentifier = "ios-arm64_i386_x86_64-simulator"
            linkerOpts(nativeFrameworkPaths.map { "-F$it/$libraryIdentifier" })
            linkerOpts("-ObjC")    
        }
    }
}

I feel like we need a way to tell gradle where to look for the SPM Firebase frameworks, or should that be handled automatically somehow? Am I missing a step somewhere?

suntrix commented 2 years ago

Have you tried to not use XCFramework()?

This is how I have it setup in our app that uses this lib.

ios {
    binaries {
        framework {
            baseName = "KmmShared"
            isStatic = true
            linkerOpts("-ObjC")    
        }
    }
}

Then I just add it to iOS app with -framework KmmShared & framework search paths in build settings.

Theoretically it should also work as dynamic framework (without isStatic = true).

gchristov commented 2 years ago

How are you installing the Firebase SDK into your iOS project? Is it with Swift Package Manager?

I tried your suggestion above the I'm still getting ld: framework not found FirebaseFirestore when I install Firebase through Swift Package Manager.

The only thing that works for me is the code that I shared here, which makes me think that Kotlin has no access to the downloaded Firebase SDK if I use SPM for some reason.

suntrix commented 2 years ago

Yes we're using SPM in our project & no linking issues in iOS. Are you sure it's actually linked in iOS project?

There's also another thing why this could happen, if you're using iOS-specific SDKs in your iosMain sources (that would actually explain the issue).

gchristov commented 2 years ago

I'm using the standard Xcode -> File -> Add package -> https://github.com/firebase/firebase-ios-sdk and I choose my project target. Xcode also seems to show autocompletes when I try to import Firebase for example. I can't launch the app because KMM isn't able to link due to the above error.

if you're using iOS-specific SDKs in your iosMain sources

I don't have iOS-specific SDKs but I do have some dependencies which have iOS implementations, for example Ktor which needs:

val iosMain by getting {
  dependencies {
    implementation("io.ktor:ktor-client-ios:$ktorVersion")
  }
}

Is this what you meant? Or you meant a Firebase iOS-specific dependency?

In your project, do you have any linker flags set after adding Firebase or are you just following the official instructions?

gchristov commented 2 years ago

Also, when I build on Xcode I can see the Firebase dependencies also building so I'd say the SDK should be linked

Screenshot 2022-04-25 at 23 27 10
suntrix commented 2 years ago

I'm using the standard Xcode -> File -> Add package -> https://github.com/firebase/firebase-ios-sdk and I choose my project target. Xcode also seems to show autocompletes when I try to import Firebase for example. I can't launch the app because KMM isn't able to link due to the above error.

if you're using iOS-specific SDKs in your iosMain sources

I don't have iOS-specific SDKs but I do have some dependencies which have iOS implementations, for example Ktor which needs:

val iosMain by getting {
  dependencies {
    implementation("io.ktor:ktor-client-ios:$ktorVersion")
  }
}

Is this what you meant? Or you meant a Firebase iOS-specific dependency?

I meant Firebase iOS-specific SDKs. I remember that I had to link the iOS frameworks when we used to have some Kotlin code in iosMain sources that were calling the Firebase SDKs directly or through the .ios properties in the multiplatform code.

In your project, do you have any linker flags set after adding Firebase or are you just following the official instructions?

Yes, we followed the official instructions.

RZahr commented 2 years ago

I am having the same issue, i followed the ios instructions and receiving ld: framework not found FirebaseFirestore

gchristov commented 2 years ago

I don’t use iOS specific SDKs in iosMain and am just linking this library in commonMain and accessing Firestore from the shared code.

Obviously on Android we have to specifically link and initialise Firebase and iOS should be the same, so I am expecting to have to tell the Kotlin compiler where to look for the dependencies during the emberAndSignXcodeFramework task, so there must be a step we’re missing somewhere…

@suntrix Do you have a working project we can take a look at?

My plan is to try and setup a basic app with just this library to rule out any multimodule-specific issues I may be hitting.

RZahr commented 2 years ago

yes a simple sample will solve this issue. Trying to solve this for few days now using cocoapods

gchristov commented 2 years ago

I tried this with a new vanilla project and am getting the same error. Steps to reproduce are:

  1. Create new Kotlin Multiplatform App from Android Studio
  2. Select Regular Framework for the setting iOS Framework Distribution
  3. Add classpath("com.google.gms:google-services:4.3.10") to the dependencies section of buildscript under MyApplication/build.gradle.kts
  4. Add apply(plugin = "com.google.gms.google-services") and implementation(platform("com.google.firebase:firebase-bom:30.0.1")) to appAndroid/build.gradle.kts
  5. Add dependencies { implementation("dev.gitlive:firebase-firestore:1.6.1") } to commonMain in shared/build.gradle.kts

At this point the Android app builds but complains about a missing google-services.json which is expected and not an issue.

  1. Open File -> Add Packages on Xcode and add https://github.com/firebase/firebase-ios-sdk
  2. Select FirebaseFirestore from the available SDKs and wait until Xcode finishes initialising
  3. Build ios app -> Fails with ld: framework not found FirebaseFirestore

@suntrix @nbransby My suspicion is that we need extra steps for the iOS setup but I can't quite figure out what they are. Any other suggestions or maybe a small working example app?

suntrix commented 2 years ago

I can repro this issue when I setup a sample app. I'll check what I did in our project to not see this issue & let you know.

suntrix commented 2 years ago

When I set the framework to static the linker issue goes away.

kotlin {
    android()

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = "AppShared"
            isStatic = true
        }
    }

    sourceSets {
        ...
    }
suntrix commented 2 years ago

Also one of the devs in my team have created https://github.com/akaffenberger/firebase-ios-sdk-xcframeworks :)

RZahr commented 2 years ago

isStatic = true did the magic ! also note that this dev.gitlive:firebase-config is causing another issue on ios but im just commenting its use for now

gchristov commented 2 years ago

I can confirm that setting isStatic = true has solved it both in my test project and real multi-module app, thanks! 🙌 @suntrix would you mind sharing a 1-line sentence of why this is necessary?

As a side-effect I assume this means the final shared KMM framework that is linked to iOS is now a static one, rather than dynamic, right?

suntrix commented 2 years ago

So essentially we have 2 ways of doing this (with their own pros/cons):

TBH - linking statically something that needs to be initialised at app start makes sense, because Apple doesn't recommend using too many dynamic frameworks in iOS apps & also there's noticeable latency when loading a bunch of dynamic frameworks at app start.

gchristov commented 2 years ago

Awesome, thanks for the clarifications!

I think it might be helpful to update the README with some setup instructions, since there are a few similar issues. I've linked some basic instructions here which as of today produce a working project (have also linked the relevant versions I'm using) 👍

I'll close this for now as I can confirm my project works both on the Intel Mac and on the M1.

suntrix commented 2 years ago

That would be a question for @nbransby since he's the owner of this project, I'm just contributing my free time :)

The issue is not specific to this library but to KMM which still is missing a few things.

clarksandholtz commented 2 years ago

WOW THIS THREAD IS A LIFE SAVER.

Thanks a bunch @suntrix and @gchristov for finding the fix and posting it here!

I think we should definitely get this stuff added to the README as it is essential to get the project working and googling for the errors this solves leads to old posts with answers that didn't work for me.

Jbass-101 commented 1 year ago

Thank the heavems for this thread!

tekinalper commented 1 year ago

Any possible way to create dynamic XCFramework(isStatic = false) with this lib?

pmodernme commented 1 year ago

Any possible way to create dynamic XCFramework(isStatic = false) with this lib?

isStatic = true results in many thousands of warnings in XCode during Generate YourApp.app.dSym such as the following

warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop.NativePointed#<get-rawPtr>(){}kotlin.native.internal.NativePtr
warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop#interpretOpaquePointed(kotlin.native.internal.NativePtr){}kotlinx.cinterop.NativePointed
warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop.CPointer#<get-value>(){}kotlin.native.internal.NonNullNativePtr
warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop#<get-ptr>__at__0:0(){0§<kotlinx.cinterop.CPointed>}kotlinx.cinterop.CPointer<0:0>
warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop#<get-rawValue>__at__kotlinx.cinterop.CPointer<*>?(){}kotlin.native.internal.NativePtr
warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop.CVariable.Type#<init>(kotlin.Long;kotlin.Int){}
warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop.CVariable.Type#<get-size>(){}kotlin.Long
warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop.CVariable.Type#<get-align>(){}kotlin.Int
warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop.CStructVar.Type#<init>(kotlin.Long;kotlin.Int){}
warning: (arm64)  could not find object file symbol for symbol _kfun:kotlinx.cinterop.NativePlacement#alloc(kotlin.Int;kotlin.Int){}kotlinx.cinterop.NativePointed

Because of the sheer quantity of warnings, it causes XCode to hang. Any idea of how to silence these warnings at least?

ubuntudroid commented 1 year ago

Hm, I don't just get those warnings while generating dsyms, I get them for each build... And yes, it seems to be a bit much for Xcode, for my little project its 36k+ warnings. 😅

pmodernme commented 1 year ago

I found a workaround that eliminates the warnings, though it is not ideal.

In your project's build settings, find Debug Information Format and under Debug select DWARF and leave Release as DWARF with dSYM File.

This eliminates the warnings, but it isn't ideal for the stack trace during debugging, I think.

ubuntudroid commented 1 year ago

This eliminates the warnings, but it isn't ideal for the stack trace during debugging, I think.

Yes, it makes setting breakpoints for Kotlin/native code in Android Studio etc. impossible.

The warnings are only a problem for Xcode, not Android Studio (go figure 😁 ), so what I'm doing now is enabling the setting in Xcode while implementing Kotlin/native stuff, then build and debug via Android Studio, and then disable DWARF with dSYM again when back in Xcode. Tedious, but it works.

Another side note: I found, that the warnings don't seem to affect Xcode as long as I don't have the problem view (or the build view) open during build.

vladmircan commented 9 months ago

I'm using the standard Xcode -> File -> Add package -> https://github.com/firebase/firebase-ios-sdk and I choose my project target. Xcode also seems to show autocompletes when I try to import Firebase for example. I can't launch the app because KMM isn't able to link due to the above error.

if you're using iOS-specific SDKs in your iosMain sources

I don't have iOS-specific SDKs but I do have some dependencies which have iOS implementations, for example Ktor which needs:

val iosMain by getting {
  dependencies {
    implementation("io.ktor:ktor-client-ios:$ktorVersion")
  }
}

Is this what you meant? Or you meant a Firebase iOS-specific dependency?

I meant Firebase iOS-specific SDKs. I remember that I had to link the iOS frameworks when we used to have some Kotlin code in iosMain sources that were calling the Firebase SDKs directly or through the .ios properties in the multiplatform code.

In your project, do you have any linker flags set after adding Firebase or are you just following the official instructions?

Yes, we followed the official instructions.

@suntrix Apologies for coming back to this after such a long time. I'm in the process of migrating my project to KMM using the gitlive firebase sdk and I'm having trouble accessing the .ios property inside the iosMain module. This seems to happen because I'm using SPM instead of CocoaPods and I'm really struggling to find a fix. According to your comment, you had a similar issue and I was wondering if you somehow remember what it was. Thanks!

walkingbrad commented 9 months ago

I believe this is due to the iOS arch "commonization" feature not being enabled. Right now, your IDE isn't showing autocomplete and is showing errors, but if the code were correct, it would compile via the command line. If you were to create a file under a specific iOS arch folder like iosArm64 vs iosMain, then the IDE would be happy. However, you can enable commonization so that the IDE is happy while writing in iosMain.

Try adding this line to your gradle.properties file at your project root:

kotlin.mpp.enableCInteropCommonization=true
vladmircan commented 8 months ago

@walkingbrad That did the trick! Thank you so much, you're a life saver!