12

I am using the WebKit framework's WKWebView and when attempting to submit to the app store, I get this error:

Your app contains non-public API usage. Please review the errors, correct them, and resubmit your application.
The app links to non-public libraries in Payload...: /System/Library/PrivateFrameworks/WebKit.framework/WebKit

If I set the deployment target to 8.0, then the error goes away, but I would like to support iOS 7 as well. The error appears to be the result of iOS7 also having the WebKit framework, but it was private back then.

I would like to therefore link to the WebKit library dynamically. How can I do this in XCode?

weiyin
  • 6,819
  • 4
  • 47
  • 58
  • You will need to fall back on UIWebView when your users are on iOS 7 (it's not clear that you were trying that). This should work: http://stackoverflow.com/q/25341238/1580288 – EthanB Sep 18 '14 at 13:44
  • I do runtime checks for ios7 and use UIWebView. It runs fine on my development devices. The problem I'm having is trying to pass validation. Thanks! – weiyin Sep 18 '14 at 15:27
  • I am having the same problem. I only use WebKit on iOS 8 but my app deploys to iOS 7 – Mike Vosseller Sep 18 '14 at 21:24

1 Answers1

16

Well, finally got this working after many hours of frustration.

Until Apple fixes the validation, the key is to dynamically link to the WebKit framework at runtime. Your project should already be using runtime checks to gracefully fall back to UIWebView for iOS7 and earlier, i.e. checking for [WKWebView class].

Step 1: Remove the WebKit framework from the project settings. Go to your target -> General -> Linked Frameworks and Libraries, and remove WebKit. At this point, your code will compile but fail to link because it cannot resolve the WKWebView and associated symbols.

Step 2: Edit your main.m file to load the library dynamically:

#import <UIKit/UIKit.h>
#import <TargetConditionals.h>
#import <dlfcn.h>
#import "MyAppDelegate.h"

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

int main(int argc, char * argv[])
{
    @autoreleasepool {
        // Dynamically load WebKit if iOS version >= 8
        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
#if TARGET_IPHONE_SIMULATOR
            NSString *frameworkPath = [[NSProcessInfo processInfo] environment][@"DYLD_FALLBACK_FRAMEWORK_PATH"];
            if (frameworkPath) {
                NSString webkitLibraryPath = [NSString pathWithComponents:@[frameworkPath, @"WebKit.framework", @"WebKit"]];
                dlopen([webkitLibraryPath cStringUsingEncoding:NSUTF8StringEncoding], RTLD_LAZY);
            }
#else
            dlopen("/System/Library/Frameworks/WebKit.framework/WebKit", RTLD_LAZY);
#endif
        }

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));
    }
}

I use a runtime OS version check because Apple only allow dynamic library loading starting in iOS 8. The library location is different for the simulator vs actual devices, so I use conditional compilation to check for that.

Step 3: Since the library is loaded dynamically, calling [WKWebView class] and [WKWebView alloc] won't work. Go through your code, changing every instance of

[WKWebView class]
// change to:
NSClassFromString(@"WKWebView")

And change every time you allocate a WKWebView:

[WKWebView alloc]
// change to:
[NSClassFromString(@"WKWebView") alloc]

You must also do this for the associated classes, including WKWebViewConfiguration, WKProcessPool, WKUserScript, and whatever others you are using. Check your linker errors for anything you may have missed.

Step 4: Now your code should successfully compile. Package, submit to the app store, and celebrate.

weiyin
  • 6,819
  • 4
  • 47
  • 58
  • For step #2, an alternative method is to add the library as optional, instead of required if needed to support iOS7. – jianhua Sep 24 '14 at 06:48
  • The validation error comes back when I add WebKit.framework as optional. But turns out if you've moved everything to NSClassFromString then telling the linker about the classes is not necessary at all! I'm updating to remove the unnecessary step. – weiyin Sep 24 '14 at 15:37
  • I meet same problem. Thx for the approach. @weiyin Since the library is loaded dynamically, it's not so convenience to use WKWebView and the associated classes. I've written an Invocation class to perform selectors with any type of parameters. Hope helping others. [link][1] [1]: https://github.com/jnuhwb/WBInvocation – Wellbin Huang Feb 10 '18 at 15:46
  • @WellbinHuang You can still import the WebKit headers and use their selectors normally. It is okay to compile with WebKit, just not link against it. – weiyin Feb 10 '18 at 16:56
  • Save my live! Thx. – Wellbin Huang Feb 11 '18 at 07:08