swiftxcodefirebaseswift-package-managerxcframework

How to wrap a binary xcframework Firebase in an SPM package and use it in a project?


I'm trying to add Firebase as a binary to my project to reduce build time, so far I've tried making my own binary and adding it to the project and it was successful, however, I've run into a problem with a big dependency like Firebase. I've downloaded Firebase binaries from their releases page, then created a new Swift Package, copied some of the xcframeworks into artifacts folder, and then added to Package.swift to test if I could use FirebaseAnalytics like this:

let package = Package(
    name: "FirebaseWrapper",
    products: [
        .library(
            name: "FirebaseWrapper",
            targets: ["FirebaseWrapper"]),
        .library(
            name: "FirebaseAnalytics",
            targets: ["FirebaseAnalytics"]),
    ],
    dependencies: [
    ],
    targets: [
        .target(
            name: "FirebaseWrapper",
            dependencies: [.target(name: "FirebaseAnalytics")]),
        .binaryTarget(name: "FirebaseAnalytics", path: "artifacts/FirebaseAnalytics/FirebaseAnalytics.xcframework"),
        .binaryTarget(name: "FBLPromises", path: "artifacts/FirebaseAnalytics/FBLPromises.xcframework"),
        .binaryTarget(name: "FirebaseAnalyticsSwift", path: "artifacts/FirebaseAnalytics/FirebaseAnalyticsSwift.xcframework"),
        .binaryTarget(name: "FirebaseCore", path: "artifacts/FirebaseAnalytics/FirebaseCore.xcframework"),
        .binaryTarget(name: "FirebaseCoreInternal", path: "artifacts/FirebaseAnalytics/FirebaseCoreInternal.xcframework"),
        .binaryTarget(name: "FirebaseInstallations", path: "artifacts/FirebaseAnalytics/FirebaseInstallations.xcframework"),
        .binaryTarget(name: "GoogleAppMeasurement", path: "artifacts/FirebaseAnalytics/GoogleAppMeasurement.xcframework"),
        .binaryTarget(name: "GoogleAppMeasurementIdentitySupport", path: "artifacts/FirebaseAnalytics/GoogleAppMeasurementIdentitySupport.xcframework"),
        .binaryTarget(name: "GoogleUtilities", path: "artifacts/FirebaseAnalytics/GoogleUtilities.xcframework"),
        .binaryTarget(name: "nanopb", path: "artifacts/FirebaseAnalytics/nanopb.xcframework")
    ]
)

Then I've added this FirebaseWrapper to "Link Binary With Libraries" stage as usual and tried to build, but Xcode gave me a bunch of errors like

Undefined symbol: _APMAnalyticsConfiguration
Undefined symbol: _APMAppMeasurementOriginFirebase
...

When I expand the detailed log, I see

ld: warning: Could not find or use auto-linked framework 'GoogleAppMeasurement'
Undefined symbols for architecture arm64:
  "_APMAnalyticsConfiguration", referenced from:
      +[FIRAnalytics startWithConfiguration:options:] in FirebaseAnalytics(FIRAnalytics.o)
  "_APMAppMeasurementOriginFirebase", referenced from:
      +[FIRAnalytics startWithConfiguration:options:] in FirebaseAnalytics(FIRAnalytics.o)
...
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

So it's obvious that I have to do some additional steps when I add some complex binary like this, or I messed up somewhere.

You can find the test project with all these packages to play with here: github link.

I'd like to ask for advice on how can I fix that, as well as any recommendations for articles/videos/guides on how xcode build system works, how are frameworks/libraries/etc arranged, and so on. So far I've watched these WWDC sessions: WWDC_1, WWDC_2, and read the docs from Apple: doc_1, doc_2, doc_3, but they gave me only somewhat shallow understanding of what's going on.


Solution

  • It was my bad, as it turned out I just had to add all the dependencies of FirebaseAnalytics to the dependencies of the main target of my FirebaseWrapper:

    Before:
    
    targets: [
        .target(
            name: "FirebaseWrapper",
            dependencies: [
                .target(name: "FirebaseAnalytics")
            ]),
    ...
    _____________________________________________________________
    
    After:
    
    targets: [
        .target(
            name: "FirebaseWrapper",
            dependencies: [
                .target(name: "FirebaseAnalytics"),
                .target(name: "FBLPromises"),
                .target(name: "FirebaseAnalyticsSwift"),
                .target(name: "FirebaseCore"),
                .target(name: "FirebaseCoreInternal"),
                .target(name: "FirebaseInstallations"),
                .target(name: "GoogleAppMeasurement"),
                .target(name: "GoogleAppMeasurementIdentitySupport"),
                .target(name: "GoogleUtilities"),
                .target(name: "nanopb"),
            ]),
    ...
    

    Now it builds and runs successfully.