Search code examples
iosjavascriptcore

JavaScriptCore framework availability on iOS


I am using the JavaScriptCore framework that was introduced in iOS 7. My app has an iOS 6.0 deployment target. I am linking the app with JavaScriptCore.framework (which is by default in required mode). The new JavaScript Objective-C classes (JSContext, JSValue etc.) are obviously not available when running on iOS 6 but I can still use the plain C API, i.e. JSGlobalContextCreate(), JSObjectCallAsFunction() etc.

If I link the MultipeerConnectivity framework which was also introduced in iOS 7 and I run my app on iOS 6, I get an expected crash at startup:

dyld: Library not loaded: /System/Library/Frameworks/MultipeerConnectivity.framework/MultipeerConnectivity
  Referenced from: /var/mobile/Applications/5AB83411-CEFC-437D-88F8-6B80C36CCE9F/MyApp.app/MyApp
  Reason: image not found

Why is my app not crashing with the same dyld error when I link the JavaScriptCore framework?


Solution

  • When linking the JavaScriptCore framework with an iOS 7 deployment target the app links to Frameworks/JavaScriptCore.framework:

    otool -L -arch armv7 MyApp 
    MyApp:
        /System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore (compatibility version 1.0.0, current version 537.51.2)
        /System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 2935.137.0)
        ...
    

    When linking the JavaScriptCore framework with an iOS 6 deployment target the app links to PrivateFrameworks/JavaScriptCore.framework:

    otool -L -arch armv7 MyApp
    MyApp:
        /System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore (compatibility version 1.0.0, current version 537.51.2)
        /System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 2935.137.0)
        ...
    

    This is because the JavaScriptCore framework has special symbols that instruct the linker to change its dylib install path depending on the deployment target version:

    nm JavaScriptCore.framework/JavaScriptCore | grep '\$ld\$'
    00000000003959a0 S $ld$install_name$os4.3$/System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore
    00000000003959a1 S $ld$install_name$os5.0$/System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore
    00000000003959a2 S $ld$install_name$os5.1$/System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore
    00000000003959a3 S $ld$install_name$os6.0$/System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore
    00000000003959a4 S $ld$install_name$os6.1$/System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore
    

    The JavaScriptCore framework is available in the PrivateFrameworks directory in iOS 4.3 to 6.1 so the app finds it there at startup. On iOS 7, PrivateFrameworks/JavaScriptCore.framework is a symbolic link to Frameworks/JavaScriptCore.framework so that the app can run on both iOS 6 and 7.

    Although not documented anywhere, this was clearly a deliberate move from Apple to add backward compatibility to the JavaScriptCore framework. But unfortunately, the App Store validation team did not get the memo from the JavaScriptCore team and apps linking the JavaScriptCore framework and targeting iOS below 7 will be rejected because they are using a framework in the PrivateFrameworks directory. Please duplicate rdar://problem/17076670 if you want Apple to solve this issue.

    In the meantime, you can workaround the rejection by removing the JavaScriptCore framework from your project and using the following Other Linker Flags instead (adapt for each undefined symbol error you get)

    -Wl,-U,_JSGlobalContextCreate
    -Wl,-U,_JSGlobalContextRelease
    

    This tells the linker to resolve the symbols dynamically. Since the JavaScriptCore framework is always indirectly loaded through UIKit → WebKit → JavaScriptCore, the symbols will resolve just fine at runtime and your app should not be rejected since it doesn’t explicitly link the JavaScriptCore framework anymore.