https://kotlinlang.org logo
Title
p

Paddy O'Brien

02/02/2023, 5:15 PM
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.
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

Landry Norris

02/02/2023, 5:18 PM
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

Adam Vastopa

02/02/2023, 5:22 PM
They are compiler errors:
> 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

Landry Norris

02/02/2023, 5:22 PM
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

Paddy O'Brien

02/02/2023, 5:23 PM
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

Landry Norris

02/02/2023, 5:24 PM
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

Paddy O'Brien

02/02/2023, 5:25 PM
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
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

Landry Norris

02/02/2023, 5:29 PM
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

Paddy O'Brien

02/02/2023, 5:29 PM
iosSimulatorArm64
l

Landry Norris

02/02/2023, 5:29 PM
Does it work when you build for iosArm64?
p

Paddy O'Brien

02/02/2023, 5:30 PM
good question
Same error
l

Landry Norris

02/02/2023, 5:37 PM
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

Paddy O'Brien

02/02/2023, 5:38 PM
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

Landry Norris

02/02/2023, 5:38 PM
I think you can also specify a Swift module instead of headers.
p

Paddy O'Brien

02/02/2023, 5:38 PM
That also had issues 😄
l

Landry Norris

02/02/2023, 5:39 PM
Most frameworks have a single header file that includes the rest. You should only need that and header filter.
p

Paddy O'Brien

02/02/2023, 5:39 PM
yeah we included the umbrella header havent found docs on headerFilter
l

Landry Norris

02/02/2023, 5:40 PM
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

Adam Vastopa

02/02/2023, 5:40 PM
adding
headerFilter = StripeTerminal/**
resulted in the same Unresolved reference error
p

Paddy O'Brien

02/02/2023, 5:41 PM
can you explain what you mean by flattened in this context?
l

Landry Norris

02/02/2023, 5:42 PM
You’ll sometimes see a structure like
Foo.h
Bar/Baz.h
Bar/FooBar.h
Baz/FooBaz.h
and frameworks sometimes flatten it to
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

Paddy O'Brien

02/02/2023, 5:43 PM
2 secs and I’ll get the error again
ahh yeah, it fails in finding the headers in the umbrella header
> 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

Landry Norris

02/02/2023, 5:46 PM
The path is likely actually {framework path}/Headers/SCPAddress.h
p

Paddy O'Brien

02/02/2023, 5:47 PM
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

Landry Norris

02/02/2023, 5:48 PM
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

Paddy O'Brien

02/02/2023, 5:49 PM
we are dealing with binaries that have specific differences between sim and device 😅
fun times in hardware driver land
l

Landry Norris

02/02/2023, 5:49 PM
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

Paddy O'Brien

02/02/2023, 5:50 PM
Ive been an Objc dev for almost 20 years this specific setup hasnt changed in a long time 😄
yeah im gonna try that.
l

Landry Norris

02/02/2023, 5:51 PM
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

Paddy O'Brien

02/02/2023, 5:53 PM
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

Landry Norris

02/02/2023, 5:56 PM
That makes sense. Wonder if this is a bug in cinterop then.
p

Paddy O'Brien

02/02/2023, 6:01 PM
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

Landry Norris

02/02/2023, 6:22 PM
No. I just do compiler opts and linker opts.
p

Paddy O'Brien

02/02/2023, 6:23 PM
👍 Your assistance got us past our. compiler woes and no we have linker ones so I thought id ask 😄
l

Landry Norris

02/02/2023, 6:23 PM
I think libraryPaths gets converted to compiler opts, but I never trust black boxes and cinterop doesn’t have great public documentation.
p

Paddy O'Brien

02/02/2023, 6:24 PM
no it doesnt
l

Landry Norris

02/02/2023, 6:24 PM
Make sure the frameworks get embedded. They have to be present at runtime.
p

Paddy O'Brien

02/02/2023, 6:25 PM
does cinterop not perform that for you?
i guess not being a dynamic framework
l

Landry Norris

02/02/2023, 6:26 PM
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

Paddy O'Brien

02/02/2023, 6:26 PM
dynamic
l

Landry Norris

02/02/2023, 6:28 PM
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.