Resolving Mac Appstore rejections due to deprecation
Fix dependencies on deprecated APIs with a bit of surgery
A couple of weeks ago I submitted an update for an app of mine to the Mac appstore and received a rejection letter like the following:
Your app uses or references the following non-public or deprecated APIs:
Contents/Java/jre/lib/libsplashscreen.dylib
Classes:
• JNFRunLoop
Contents/Java/jre/lib/libosx.dylib
Classes:
• JNFRunLoop
• JNFException
Symbols:
• _JNFNormalizedNSStringForPath
• _JNFNativeMethodEnter
• _JNFNativeMethodExit
• _JNFNormalizedJavaStringForPath
Contents/Java/jre/lib/libosxapp.dylib
Classes:
• JNFRunLoop
Symbols:
• _JNFNSToJavaString
• _JNFRunLoopDidStartNotification
• _JNFCallStaticObjectMethod
• _JNFJavaToNSString
Contents/Java/jre/lib/libosxui.dylib
Classes:
• JNFRunLoop
• JNFException
• JNFJObjectWrapper
Symbols:
• _JNFNSToJavaString
• _JNFNativeMethodEnter
• _JNFNSTimeIntervalToJavaMillis
• _JNFNativeMethodExit
• _JNFCallVoidMethod
Contents/Java/jre/lib/libawt_lwawt.dylib
Classes:
• JNFRunLoop
• JNFException
• JNFJObjectWrapper
• JNFWeakJObjectWrapper
Symbols:
• _JNFSetLongField
• _JNFGetStringUTF16UniChars
• _JNFReleaseStringUTF16UniChars
• _JNFCallDoubleMethod
• _JNFNSToJavaString
• _JNFCallLongMethod
• _JNFDeleteWeakGlobalRef
• _JNFGetStaticObjectField
• _kNoSuchMethodException
• _kIllegalArgumentException
• _JNFCallStaticVoidMethod
• _JNFPerformEnvBlock
• _JNFCallObjectMethod
• _JNFDumpJavaStack
• _JNFNewObjectArray
• _JNFIsInstanceOf
• _JNFNewGlobalRef
• _JNFDeleteGlobalRef
• _JNFCallStaticObjectMethod
• _JNFCallStaticIntMethod
• _JNFNormalizedNSStringForPath
• _JNFGetLongField
• _kOutOfMemoryError
• _JNFNewWeakGlobalRef
• _JNFNewObject
• _JNFNewObject
• _kRuntimeException
• _JNFCallIntMethod
• _JNFNativeMethodEnter
• _JNFGetIntField
• _JNFGetObjectField
• _JNFNativeMethodExit
• _JNFJavaToNSString
• _JNFCallVoidMethod
• _JNFJavaToNSNumber
• _JNFNormalizedJavaStringForPath
• _JNFCallStaticBooleanMethod
• _JNFSetObjectField
• _JNFNewIntArray
• _JNFCallBooleanMethod
• _JNFCallStaticLongMethod
Contents/Java/jre/lib/libsaproc.dylib
Classes:
• JNFException
Symbols:
• _JNFNativeMethodEnter
• _JNFNativeMethodExit
• _JNFJavaToNSString
Contents/Java/jre/lib/libosxkrb5.dylib
Classes:
• JNFException
• JNFTypeCoercer
• JNFDefaultCoercions
Symbols:
• _JNFCallStaticVoidMethod
• _JNFPerformEnvBlock
• _JNFCallObjectMethod
• _JNFNewObject
• _JNFNativeMethodEnter
• _JNFNativeMethodExit
• _JNFCallBooleanMethod
Contents/Java/jre/lib/libosxsecurity.dylib
Classes:
• JNFException
Symbols:
• _JNFNativeMethodEnter
• _JNFNativeMethodExit
• _JNFJavaToNSString
• _JNFCallVoidMethod
The use of non-public or deprecated APIs is not permitted on the App Store, as they can lead to a poor user experience should these APIs change and are otherwise not supported on Apple platforms.
You may have received a similar rejection letter if your app bundled an older version of Java (circa JDK 11) with it. This is some features of the JDK, on Mac, depend on the JavaNativeFoundation framework, which Apple has recently, quietly deprecated.
In a perfect world, I would just bundle a newer version of Java and be done with it, but this this app had problems on newer versions that I didn’t have the time to solve this minute - and I wanted to get an update out quickly to fix a pretty critical bug that was affecting user experience. So, I decided to perform some surgery on the app and its bundled JRE.
Luckily, Apple has open sourced the JavaNativeFoundation framework and made it available on GitHub, so I was able to build my own version without much difficulty. The next challenge was to inject my version of the framework into my app bundle, and update the dependencies of all of the libs in the JRE to use this version instead of the system version.
You can list the dependencies of a dylib using otool -L /path/do/my.dylib
, so I wrote a little bash script that finds all of the libs in my bundle, and calls otool -L
on each one to see which ones depend on JavaNativeFoundation.
When I ran this script, it spat out a list of dylibs in the jre/libs directory of my app. At this point, the problem was tractible. All I had to do was build my own JavaNativeFoundation.framework, copy it into my app bundle, and then update these specific dylibs to point to this version instead of the system version.
Luckily MacOS ships with another nifty tool called `install_name_tool` that can update dependencies in dynamic libraries, so it was fairly straight forward to write a script that performed the entire surgery.
The script works like:
bash update_jnf_linkage.sh \
/path/to/JavaNativeFoundation.framework.zip \
/path/to/MyApp.app
After running this script, I ran my first script (which runs otool -L on all of the dylibs in my bundle) again to confirm that all of the links had been updated correctly, and ran my app through a guantlet of tests to ensure that nothing broke.
All was well so I tried to submit this new version to the app store.
There was one more hurdle I had to clear before they would accept it. I had to change the bundle ID of the JavaNativeFoundation framework because Apple won’t allow me to include bundles in their namespace (i.e. starting with com.apple). I changed it to something in my own namespace, and resubmitted. And it was accepted.
Here are the links to my scripts, and pre-build JavaNativeFoundation framework zip file that I used, in case you are facing a similar issue.
Ok I found a solution if anyones interested - JNA expands itself at a runtime and mac labels it a malware, as it should. The solution is to pre-expand JNA, check it into a library bunbdle and set runtime flag where the library location is. took me 8 hours to understand, I thought it was darwin vs 32/64 arch issues
I started digging and it looks like its a NSObject issue, I believe the code originated from you many years ago
public NativeFileDialog(final String title,
final int mode)
{
super( "NSObject" ); <<--- fails here
* @author shannah
* @see <a
* href="https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/nssavepanel_Class/Reference/Reference.html">NSSavePanel
* Class Reference</a>