Im trying to build a cinterop .def for Stripe Ter...
# kotlin-native
p
Im trying to build a cinterop .def for Stripe Terminal, which is an XCFramework. From what I can find online my def file looks good but I am getting “Unresolved Reference” errors in the code that imports symbols from it Ive tried the setup from https://kotlinlang.org/docs/multiplatform-mobile-ios-dependencies.html#87bb9559 and run into the same issues save that with what I have below it also gets unresolved errors on the package Wondering if anyones got a better def file setup for xcframeworks or can spot obvious errors.
Copy code
language = Objective-C
package = StripeTerminal
compilerOpts = -framework StripeTerminal -DNS_FORMAT_ARGUMENT(A)= -D_Nullable_result=_Nullable
linkerOpts = -framework StripeTerminal -framework CoreBluetooth -framework ExternalAccessory -framework CoreLocation -framework Security -framework Foundation

# M1 Simulator
compilerOpts.ios_simulator_arm64 = -FiOSLibs/StripeTerminal.xcframework/ios-arm64_x86_64-simulator/StripeTerminal.framework
linkerOpts.ios_simulator_arm64 = -FiOSLibs/StripeTerminal.xcframework/ios-arm64_x86_64-simulator/StripeTerminal.framework

# X86 Simulator
compilerOpts.ios_x64 = -FiOSLibs/StripeTerminal.xcframework/ios-arm64_x86_64-simulator/StripeTerminal.framework
linkerOpts.ios_x64 = -FiOSLibs/StripeTerminal.xcframework/ios-arm64_x86_64-simulator/StripeTerminal.framework

# Device
compilerOpts.ios_arm64 = -FiOSLibs/StripeTerminal.xcframework/ios-arm64/StripeTerminal.framework
linkerOpts.ios_arm64 = -FiOSLibs/StripeTerminal.xcframework/ios-arm64/StripeTerminal.framework
l
When you say unresolved reference, are you referring to IDE errors, or link time errors? If it’s in the IDE, try creating a Test.kt file in iosArm64Main and see if you can reference everything you need.
The one thing I see is that your M1 simulator is using
ios-arm64_x86_64-simulator
. This is for ios-arm64 and x86_64-simulator (the -simulator doesn’t distribute to both), so it’s possible it’s not finding anything for M1 simulator, so the intersection of all 3 is the empty set.
a
They are compiler errors:
Copy code
> Task :PointOfSale-StripeWrapper:compileKotlinIos
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/commonMain/kotlin/com/shopify/pos/stripewrapper/StripeTerminalEventListener.kt: (365, 81): Unresolved reference: it
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeConnectionTokenCallback.kt: (3, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeConnectionTokenCallback.kt: (6, 72): Unresolved reference: SCPConnectionTokenCompletionBlock
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (4, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (5, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (6, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (7, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (8, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (9, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (10, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (11, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (12, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (13, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (14, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (15, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (16, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (17, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (18, 8): Unresolved reference: StripeTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (79, 27): Unresolved reference: SCPTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (81, 17): Unresolved reference: SCPTerminal
e: /Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt: (82, 17): Unresolved reference: SCPTerminal
/Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt:82:17: error: Unresolved reference: SCPTerminal
/Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt:88:37: error: Unresolved reference: SCPCancelable
/Users/vastopa/src/github.com/Shopify/rn-pos-2/multiplatform/PointOfSale-StripeWrapper/src/iosMain/kotlin/com/shopify/pos/stripewrapper/StripeDeviceController.kt:89:36: error: Unresolved reference: SCPCancelable
l
Just checked a project where I set up cinterop for all 3, and my ios_simulator_arm64 is using the same
ios-arm64_x86_64-simulator
suffixed framework, so that’s probably not the issue.
p
yeah the triplet I think is OS - archs - platform, ill check in lipo to be sure though
durr lipo isnt going to give us that info
l
How is your source set configuration setup? Are you using the ios() shortcut or creating all 3 manually? If using the shortcut, how are you adding simulator arm64?
p
the xcframeworks info.plist says it should be the right path
for reasons we determine the target based on command line options passed into packForXcode then set it up. this is what it looks like for us
Copy code
def iosPlatform = findProperty("kn.ios.platform") ?: "iphonesimulator"
    def iosTarget
    if (iosPlatform == "iphoneos") {
        iosTarget = iosArm64("ios")
    } else {
        iosTarget = iosSimulatorArm64("ios")
    }

    final String buildType = findProperty("kn.ios.buildType") ?: "Debug"
    def isRelease = buildType == "Release"

    iosTarget.compilations.getByName("main").cinterops.create("StripeTerminal")
We use this setup on other modules using cinterop and have no issues, but those libraries are static or xcframeworks wrapping static libraries instead of frameworks. This one is an xcframework wrapping frameworks
l
Do you happen to know which ios target it’s setting up when you get the error? Are you building for device or simulator?
p
Copy code
iosSimulatorArm64
l
Does it work when you build for iosArm64?
p
good question
Same error
l
In the project I’m referencing, we use the headers manually and provide a header search path for the folder in the framework. Linking is the same as y’all have.
I noticed you don’t specify any headers. You may want to try that first.
p
We had tried that and ran into other issues but we will look again. I think we only specified the umbrella header and it couldnt see anything else
Hoping we dont need to add all of them 😄
l
I think you can also specify a Swift module instead of headers.
p
That also had issues 😄
l
Most frameworks have a single header file that includes the rest. You should only need that and header filter.
p
yeah we included the umbrella header havent found docs on headerFilter
l
I’ve found that most frameworks flatten the header structure. I usually copy the headers to a separate folder and unflatten it as needed. HeaderFilter should work too.
a
adding
headerFilter = StripeTerminal/**
resulted in the same Unresolved reference error
p
can you explain what you mean by flattened in this context?
l
You’ll sometimes see a structure like
Copy code
Foo.h
Bar/Baz.h
Bar/FooBar.h
Baz/FooBaz.h
and frameworks sometimes flatten it to
Copy code
Foo.h
Baz.h
FooBar.h
FooBaz.h
Can you elaborate on the issues y’all had with the umbrella header? Was it complaining about headers not found?
p
2 secs and I’ll get the error again
ahh yeah, it fails in finding the headers in the umbrella header
Copy code
> Task :PointOfSale-StripeWrapper:cinteropStripeTerminalIos FAILED
Exception in thread "main" java.lang.Error: iOSLibs/StripeTerminal.xcframework/ios-arm64_x86_64-simulator/StripeTerminal.framework/Headers/StripeTerminal.h:12:9: fatal error: 'StripeTerminal/SCPAddress.h' file not found
l
The path is likely actually {framework path}/Headers/SCPAddress.h
p
I did add -I path_to_headers in the compiler opts
compilerOpts.ios_simulator_arm64 = -IiOSLibs/StripeTerminal.xcframework/ios-arm64_x86_64-simulator/StripeTerminal.framework/Headers -FiOSLibs/StripeTerminal.xcframework/ios-arm64_x86_64-simulator/StripeTerminal.framework
l
Headers tend to be the same for each framework in the xcframework, so I usually copy them into a folder with the right name. You may be able to create
headers/StripeTerminal
in your project folder, then copy the headers there.
p
we are dealing with binaries that have specific differences between sim and device 😅
fun times in hardware driver land
l
Just one of the many sneaky incompatibilities with Apple stuff. My team has a running conspiracy theory that Apple’s trying to make it harder for other technologies to interop.
headers/{platform}/StripeTerminal
and copy each one?
p
Ive been an Objc dev for almost 20 years this specific setup hasnt changed in a long time 😄
yeah im gonna try that.
l
I’m sure there’s a way to tell it to replace the folder when searching, but I’m too lazy and copying headers usually works.
p
yeah there’s two types of reference <StripeTerminal/StripeTerminal.h> and “StripeTerminal/StripeTerminal.h” The first is a framework references, the second is a file reference, and it feels like kmm is maybe treating the first like the second
l
That makes sense. Wonder if this is a bug in cinterop then.
p
Enough progress with moving things under the
StripeTerminal
to suggest that that’s what we are seeing. Our previous hack rewrote framework includes to be file includes. It’s not pretty
We were including the library in the app and staticly linking the kmm module with transitive exports. made compile times unbearable
@Landry Norris For your xcframework do you specify
libraryPaths
?
l
No. I just do compiler opts and linker opts.
p
👍 Your assistance got us past our. compiler woes and no we have linker ones so I thought id ask 😄
l
I think libraryPaths gets converted to compiler opts, but I never trust black boxes and cinterop doesn’t have great public documentation.
p
no it doesnt
l
Make sure the frameworks get embedded. They have to be present at runtime.
p
does cinterop not perform that for you?
i guess not being a dynamic framework
l
No. That’s an xcode task. I think you can go to embed frameworks in xcode and add it.
Is this static or dynamic?
p
dynamic
l
Yeah, dynamic frameworks always have to be present at runtime. There’s a few ways to do this if you’re working on a library (looks like you are). Typically, this task will be passed down to the consumer with the current state of KMM. Look at the instructions for Crashlytics with Kermit or using the firebase-kotlin-sdk.