Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Including WebKit framework for iOS8 fails validation

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?

like image 862
weiyin Avatar asked Sep 17 '14 17:09

weiyin


1 Answers

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.

like image 89
weiyin Avatar answered Oct 21 '22 14:10

weiyin