JNA issue on MacOS after publishing to TestFlight
Hello,
Thank you for so useful library.
I've faced with issue on MacOS, presumably after update of MacOS and xCode. It's shows an JNA error and then crashes. Here is the text of error:
/Users/user/Library/Containers/com.site.myapp/Data/tmp/jna9436987196270821361.tmp: dlopen(/Users/user/Library/Containers/com.site.myapp/Data/tmp/jna9436987196270821361.tmp, 0x0001): tried:
/Users/user/Library/Containers/com.site.myapp/Data/tmp/jna9436987196270821361.tmp (code signature in <BEFEF0FB-C7C5-305A-8B79-ED4851A90E7F> '/Users/user/Library/Containers/com.site.myapp/Data/tmp/jna9436987196270821361.tmp' not valid for use in process: library load disallowed by system policy),
/System/Volumes/Preboot/Cryptexes/OS/Users/user/Library/Containers/com.site.myapp/Data/tmp/jna9436987196270821361.tmp (no such file),
'/Users/user/Library/Containers/com.site.myapp/Data/tmp/jna9436987196270821361.tmp' not valid for use in process: library load disallowed by system policy
Here is a sample code:
val result = remember { mutableStateOf(listOf<Path>()) }
Text(text = result.value.joinToString("\n"))
val directoryLauncher = rememberDirectoryPickerLauncher(title = Strings.current.app_name) { directory ->
directory?.file?.absolutePath?.let { result.value = listOf(Path(it)) }
}
Button(
onClick = {
directoryLauncher.launch()
},
) {
Text(text = "Open")
}
Library version 0.8.7.
Could you please help with this issue?
Thank you.
Hello @bartwell! Thank you for your feedback!
I'm trying to reproduce your issue.
- What is your macOS and Xcode version?
- Does the sample-compose work on your device?
@vinceglb, sorry for not mentioning the key detail earlier: this issue occurs only in sandbox mode after publishing to TestFlight, so I can't reproduce it in the sample application. I am using macOS Sequoia 15.1 (24B83) and Xcode 16.1 (16B40).
Thank you for the information. Interesting 🤔
The bad news here is I don't have anymore an Apple Developer subscription to test the behavior.
I found this thread that can help us fix the problem: https://groups.google.com/g/jna-users/c/Bws1h060faA
Thank you for the link. It seems like the solution might be:
System.setProperty("jna.nounpack", "true");
System.setProperty("jna.boot.library.path", "<path to native libs>");
However, could you clarify what path should be specified here? As far as I can tell, the directories inside /Applications/MyApp.app/Contents/ are writable only by root.
I just came across the same issue and managed to solve it. The issue is due to sandboxing, there is a good article here: https://www.marcogomiero.com/posts/2024/compose-macos-app-store/
In this case, we need to place libjnidispatch.jnilib to our resources to make it "trusted" during runtime. Here is what I had to:
- Check jna version that FileKit uses in libs.versions.toml. Currently thats 5.17.0.
- Download the jna-5.17.0.jar from Github: https://github.com/java-native-access/jna?tab=readme-ov-file#jna
- Unzip jar locally. I used
unzip jna-5.17.0.jar - Get the
libjnidispatch.jnilibfrom/com/sun/jna/darwin-aarch64/libjnidispatch.jnilib. Note: I only need to suport ARM, if you needdarwin-x86-64, there is another library for that in there aswell. - You need to sign this file with your
3rd Party Mac Developer Applicationcertificate and yourruntimeEntitlements.
Your Runtime entitlements the ones set in nativeDistributions.macOS.runtimeEntitlementsFile. For more info, check the docs.
nativeDistributions {
...
macOS {
...
runtimeEntitlementsFile.set(project.file("runtime-entitlements.plist"))
}
}
With that info, you can run the following script to sign libjnidispatch.jnilib. Remember to exchange the *** with your actual certificate.
codesign -f -s "3rd Party Mac Developer Application: *** (***)" \
--entitlements runtime-entitlements.plist \
--timestamp --options runtime \
libjnidispatch.jnilib
- We can now place our signed file in a
appResourcedirectory that we can define. First we can define it innativeDistributionslike such. If your folder is calledcomposeAppthis will reference tocomposeApp/resources.
nativeDistributions {
...
appResourcesRootDir.set(project.layout.projectDirectory.dir("resources"))
}
In this resources folder, we can place resources for different architectures. In our case, we want the file to be in macos-arm64. Therefore we can finally copy libjnidispatch.jnilib to composeApp/resources/macos-arm64/libjnidispatch.jnilib.
- Lastly, whenever our app is sandboxed (e.g. run from Testflight, App Store etc.) we need to use this version of the library. We can do that like that:
fun main() {
application {
val isSandboxed = System.getenv("APP_SANDBOX_CONTAINER_ID") != null
if (isSandboxed) {
val resourcesPath = System.getProperty("compose.application.resources.dir")
System.setProperty("jna.nounpack", "true")
System.setProperty("jna.boot.library.path", resourcesPath)
}
FileKit.init(appId = "your app id")
Hope this helps anyone, if you have questions - feel free to reach out.
Thank you so much for your feedback on this issue! This is very valuable!
I'll keep this issue open until I point it out in the documentation.