Can anybody share a working example of a project w...
# multiplatform
e
Can anybody share a working example of a project which uses cinterop to pull in an iOS framework? I was never able to get Cocoapods working, so I’m trying https://kotlinlang.org/docs/multiplatform-ios-dependencies.html#add-a-framework-without-cocoapods. Got my def files set up. My frameworks have been downloaded and are sitting in the project folder, but I get errors like this for all the frameworks:
Exception in thread "main" java.lang.Error: /var/folders/97/s34y5jdx6lx7pq323l5dngxw0000gn/T/4761418149266185367.m:1:9: fatal error: module 'MapboxCommon' not found
. It would be most helpful if somebody could point me to a working example of this. Thanks.
m
Unfortunately what I have is wrapped up in a closed source project and uses a convention plugin, so it is a bit hard to share. But my
.def
file is like
Copy code
language = Objective-C
modules = MapboxCoreMaps MapboxWrapper
compilerOpts = -fmodules
Then I have this that configures the cinterop
Copy code
fun KotlinNativeTarget.createInterops(framework: PangeaFramework, defFile: File, packageName: String, interopName: String) {
    val frameworkSearchPaths = frameworkSearchPath(framework)
    val frameworkSearchPathOptions = frameworkSearchPaths.map { "-F$it" }
    with(compilations) {
        getByName("main") {
            cinterops {
                create(interopName) {
                    this.defFile = defFile
                    packageName(packageName)
                    compilerOpts(frameworkSearchPathOptions)
                }
            }
        }
    }
}
And I use the
KotlinTarget.targetName
to determine which architecture to use for the search path inside each of Mapbox's xcframeworks so that I'm interoping with a specific framework instead of the xcframework.
e
Looks like you’re trying to do something very similar to what I am. A couple of questions: • For frameworkSearchPaths, what do you do for iosX64? It doesn’t seem to have a corresponding framework under the .xcframework folder? Just skip that target? • Where did this MapboxWrapper module come from? • Why is your def file referring to MapboxCoreMaps and not MapboxMaps? I’m assuming (naively) that your Kotlin code will be calling the Mapbox public APIs. Thanks!
m
• I use
ios-arm64_x86_64-simulator
for both
iosX64
and
iosSimulatorArm64
• MapboxWrapper is a module I created that wraps Mapbox's Swift API with an Objective-C compatible API. It just wraps the functionality that we need. • MapboxMaps cannot be cineteropped because it is Swift with no Objective-C bindings. MapboxWrapper depends on it internally but not in the public API.
e
Thanks for the feedback. This suggests that I will also be needing to make my own Objective-C wrapper around the MapboxMaps calls. Is that correct?
m
I believe so, I started this project a couple of years ago, so there have been changes to both Mapbox and Kotlin. But I believe Kotlin still doesn't support Swift and Mapbox still doesn't support Objective-C.
e
Sigh. That’s rather disappointing. Thanks!
m
The other option is to do everything using MapboxCoreMaps, but that API is pretty rough and low level. We just have it included for
CustomLayerHost
.
The nice things is the public API from Mapbox between iOS and Android are very similar and I found that if there was a bug in Mapbox it typically was found on both platforms. The GLJS version of Mapbox has a very different API, but luckily I didn't need to support that.
e
Thanks for your help. I may have more questions for you as I go forward.
@Michael Krussel, thanks to your help, I’ve been able to get really close. However, the linker fails because it can’t find my frameworks:
ld: warning: Could not find or use auto-linked framework 'MapboxCoreMaps': framework 'MapboxCoreMaps' not found
Following the instructions on kotlinlang.org, I’ve added this block to my iosTargets block:
Copy code
iosTarget.binaries.all {
    // Tell the linker where the framework is located.
    linkerOpts(
      "-framework", "MapboxCoreMaps", "-Fsrc/iosFrameworks/MapboxCoreMaps.xcframework/$subDir",
      "-framework", "MapboxMaps", "-Fsrc/iosFrameworks/MapboxMaps.xcframework/$subDir",
    )
  }
Where
iosTarget
is the
KotlinNativeTarget
and
subDir
is the “ios-arm64” or “ios-arm64_x86_64-simulator” as appropriate. Any suggestions as to why it’s not finding the frameworks?
m
I didn't have the "-framework" part of the arguments in my linkerOpts.
Copy code
/**
 * Configure the project to depend on [frameworks].
 */
fun Project.dependOnFrameworks(frameworks: Iterable<PangeaFramework>) {
    tasks.withType<KotlinNativeTest> {
        environment(
            "SIMCTL_CHILD_DYLD_FRAMEWORK_PATH",
            frameworks.flatMap { frameworkSearchPath(it, targetName!!) }
                .joinToString(separator = ":")
        )
    }
    project.configureIosTargets { target ->
        val searchPaths = frameworks.flatMap { target.frameworkSearchPath(it) }.map { "-F$it" }
        target.binaries.configureEach {
            linkerOpts(searchPaths)
        }
    }
}
e
I took out the “-framework” arguments, and now it links successfully. Which is interesting. I wonder why the docs on kotlinlang.org specify those arguments for the linker. Turns out the problem was that I had the paths wrong for the framework. Now it compiles and links, but fails at runtime because it’s unable to find the library. I feel like I’m asymptotically approaching a workable build. Eventually, I did succeed in getting a working build, but it required doing the build in Xcode rather than from Gradle.
Thanks for all your help.