2

I am trying to setup a tracking library written in Kotlin Multiplatform to support all our mobile clients.

Tests for Android went well (integrating snowplow via gradle).

I also managed to integrate Snowplow via cocoapods into the MPP.

kotlin {
    ...
    cocoapods {
        ...
        pod("SnowplowTracker") {
            version = "~> 1.3.0"
        }
    }

And wrote following class in the X64 sourceset:

import cocoapods.SnowplowTracker.*
import com.tracking.domain.model.*

class X64Tracker {

    private val tracker: SPTracker

    init {
        val emitter = SPEmitter.build {
            it?.setUrlEndpoint("our.staging.endpoint")
            it?.setProtocol(SPProtocol.SPProtocolHttps)
        }

        tracker = SPTracker.build {
            emitter?.let { spEmitter -> it?.setEmitter(spEmitter) }
            it?.setBase64Encoded(false)
            it?.setSubject(SPSubject(platformContext = true, andGeoContext = true))
            it?.setAppId("MPP.test")
            it?.setApplicationContext(true)
        }
    }

    fun trackSomething() {
        track(
            eventData = getEventData(
                name = "MPP_test_event",
                appArea = EventArea.Lifecycle,
                action = EventAction.Check,
                objectType = EventObjectType.Device,
                source = EventSource.Client,
                screenName = EventScreenName.Congratulations,
            ), contexts = emptyList()
        )
    }

    private fun track(eventData: SPSelfDescribingJson, contexts: List<SPSelfDescribingJson?>) {
        try {
            val yo = SPSelfDescribing.build {
                it?.setEventData(eventData)
                it?.setContexts(contexts.toMutableList())
            }

            tracker.track(yo)
        } catch (e: IllegalStateException) {
            print("snowplow was not yet initialized when the following event occurred: $eventData")
        }
    }

    private fun getEventData(
        name: String,
        appArea: EventArea,
        action: EventAction,
        objectType: EventObjectType,
        source: EventSource,
        screenName: EventScreenName,
    ) = SPSelfDescribingJson(
        SCHEMA_EVENT, mapOf(
            PROPERTY_EVENT_NAME to name,
            PROPERTY_APP_AREA to appArea.serializedName,
            PROPERTY_ACTION to action.serializedName,
            PROPERTY_OBJECT_TYPE to objectType.serializedName,
            PROPERTY_SOURCE to source.serializedName,
            PROPERTY_SCREEN_NAME to screenName.serializedName,
        )
    )

}

Which is compiling and building our .framework files fine. Here is how we do that:

tasks.register<org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask>("debugFatFramework") {
        baseName = frameworkName + "_sim"
        group = "Universal framework"
        description = "Builds a universal (fat) debug framework"
    
        from(iosX64.binaries.getFramework("DEBUG"))
    }
    
    tasks.register<org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask>("releaseFatFramework") {
        baseName = frameworkName + "_arm"
        group = "Universal framework"
        description = "Builds a universal (release) debug framework"
    
        from(iosArm64.binaries.getFramework("RELEASE"))
    }

Afterwards we combine this into an .xcframework file using following command:

xcrun xcodebuild -create-xcframework \
    -framework tracking_arm.framework \
    -framework tracking_sim.framework \
    -output tracking.xcframework

We use Carthage to integrate it into our main app, but as soon I try to build the iOS project following error pops up:

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_SPSelfDescribing", referenced from:
      objc-class-ref in tracking_sim(result.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The weird thing: No matter which version of Snowplow I define in the cocoapods block - the syntax in my class does not need to change. Even updating to Snowplow 2.x doesn't require me to get rid of the SP prefixes.

I am not sure how to continue at all and appreciate any help.

4ndro1d
  • 2,926
  • 7
  • 35
  • 65
  • create a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) and ask it on [jetbrains issue tracker](https://youtrack.jetbrains.com/) – Phil Dukhov Aug 17 '21 at 04:37

1 Answers1

4

The following:

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_SPSelfDescribing", referenced from:
      objc-class-ref in kaiaTracking_sim(result.o)
ld: symbol(s) not found for architecture x86_64

That says the linker can't find SPSelfDescribing. I assume SPSelfDescribing is part of SnowplowTracker. You'll need to add SnowplowTracker to the iOS app.

If you were using Cocoapods to integrate Kotlin into your iOS project, the podspec generated would include SnowplowTracker as a dependency.

Since you are not using Cocoapods, you need to include it yourself. We recently had to figure out Carthage for a client. I would highly recommend moving to SPM or Cocoapods for a number of reasons, but that's a different discussion. To add SnowplowTracker with Carthage, add this to your Cartfile:

github "snowplow/snowplow-objc-tracker" ~> 1.3

Then when you update Carthage, add that to your iOS project. The linker will be able to find it.

To be clear, the Undefined symbols for architecture x86_64 error isn't complaining about Kotlin. It's saying it can't find SnowplowTracker, which you need to add to the iOS project.

Kevin Galligan
  • 16,159
  • 5
  • 42
  • 62
  • I updated my question and included how we export .framework files and include it via Carthage. We cannot migrate to cocoapods yet. – 4ndro1d Aug 05 '21 at 19:05
  • Updated. Summary, add `github "snowplow/snowplow-objc-tracker" ~> 1.3` to your Cartfile. – Kevin Galligan Aug 06 '21 at 11:38
  • Actually we do have a reference; To version 1.2 though. So I downgraded the version in my Kotlin MPP project to 1.2 as well, cleaned the build and still get the same error. – 4ndro1d Aug 06 '21 at 12:06
  • Well, I don't know what versions you need, but that error is still saying your iOS project is not configured correctly. The '.h' files declare what components the compiler can expect to exist, and when the final binary is being created, the linker attempts to find them. It can't, which means they're not added correctly. Carthage is "simple" but also relatively unsupported, and kind of not user friendly in a number of ways. You may have the dependency in your Cartfile, but it needs to then be added to your iOS project manually, etc. – Kevin Galligan Aug 07 '21 at 11:59
  • Snowplow is already used with that version in the iOS project all over the place though and works fine :/ – 4ndro1d Aug 09 '21 at 08:55