Xcode 6 + iOS 8 SDK but deploy on iOS 7 (UIWebKit & WKWebKit)
Asked Answered
C

2

18

We are creating an app using Xcode 6 beta 5 + Swift on iOS 8 SDK. We'd like to deploy to iOS 7 as well. Is that possible? When we set the deployment target of the project to 7.0, we get compile time errors like this:

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_WKPreferences", referenced from:
      __TMaCSo13WKPreferences in WebViewController.o
  "_OBJC_CLASS_$_WKWebView", referenced from:
      __TMaCSo9WKWebView in WebViewController.o
  "_OBJC_CLASS_$_WKWebViewConfiguration", referenced from:
      __TMaCSo22WKWebViewConfiguration in WebViewController.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I believe that's because we are using WKWebKit, which is supported by iOS 8+ only. We are ok with using UIWebKit for iOS 7 but WKWebKit for iOS 8. How do we define that?

Our class definition looks like this...

import WebKit

class WebViewController: UIViewController, WKNavigationDelegate {
   ...
}

and it's called by:

        let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let destinationVC = mainStoryboard.instantiateViewControllerWithIdentifier("WebViewController") as WebViewController
        presentViewController(destinationVC, animated: true, completion: nil)

I was thinking about using this fragment to call presentViewController but that doesn't solve the compile time issues. (NSFoundationVersionNumber doesn't solve compile time issues either)

    if ((UIDevice.currentDevice().systemVersion as NSString).floatValue >= 8.0) {

    } else {

    }

UPDATE: kkoltzau has the correct answer. I'm adding some more info for others.

First, go to your Project, click on General, scroll down to Linked Frameworks and Libraries, and add WebKit.framework as Optional. (I also did it for UIKit.framework) See screenshot:

enter image description here

As for my WebViewController class. It still imports UIKit and WebKit. but the viewDidLoad() sets the view based on kkoltzau's example. Then whenever I need to load/reload the web page, it checks for existence of wkWebView.

Cointreau answered 16/8, 2014 at 14:42 Comment(2)
Those are linker errors, not compiler errors. Are you getting those from Xcode 6 or Xcode 5? BTW - Do not check the iOS version to determine what to do. Instead, check to see of a given class or method exists.Tele
possible duplicate of Xcode build failure "Undefined symbols for architecture x86_64"Urana
L
18

You will need to check if WKWebView is available, and fall back to UIWebView if its not.

Make sure you weak link WebKit.framework (set to optional)

Objective-C:

WKWebView *wkWebView = nil;
UIWebView *uiWebView = nil;

Class wkWebViewClass = NSClassFromString(@"WKWebView");
if(wkWebViewClass) {
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    // ...
    wkWebView = [[wkWebViewClass alloc] initWithFrame:frame configuration:config];
    [self.view addSubview:wkWebView];
}
else {
    uiWebView = [[UIWebView alloc] initWithFrame:frame];
    [self.view addSubview:uiWebView];
}

Swift:

var wkWebView : WKWebView?
var uiWebView : UIWebView?

if NSClassFromString("WKWebView") {
    let config = WKWebViewConfiguration()
    // ...
    wkWebView = WKWebView(frame: frame, configuration: config)
    self.view.addSubview(wkWebView)
}
else {
    uiWebView = UIWebView(frame: frame)
    self.view.addSubview(uiWebView)
}

Then elsewhere in your code:

Objective-C:

if(wkWebView) {
    // WKWebView specific code
}
else {
    // UIWebView specific code
}

Swift:

if let w=wkWebView {
    // WKWebView specific code
}
else if let w=uiWebView {
    // UIWebView specific code
}
Libyan answered 16/8, 2014 at 16:25 Comment(8)
None of this addresses the question which is about linker errors.Tele
Thanks kkoltzau, let me try and report back. Interesting to know you can set class via 'NSClassFromString'Cointreau
@Tele Weak linking to WebKit.framework, mentioned in the second sentence, covers the linker error.Libyan
Thanks kkoltzau about setting WebKit.framework to optional. I can't paste screenshot, but I'll do so as an answer for others.Cointreau
My class also implements WKNavigationDelegate, including methods such as ` func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation)` and func webView(webView: WKWebView, didCommitNavigation navigation: WKNavigation). How do I include them in this example?Cointreau
The app was able to compile, but at runtime, on an iOS 8 device, it's giving me an error that wkWebView = WKWebView(frame: webViewWrapper.bounds, configuration: configuration) resulted in nil, as no WKWebView was created. I can't tell if it's because let configuration = WKWebViewConfiguration() failed or not.Cointreau
@Dean Regarding WKNavigationDelegate, just implement it as usual. I don't know why WKWebView would fail to create assuming WebKit.framework is properly linked.Libyan
@kkoltzau I understand why you would instantiate the WKWebView class from a string, but what is the reason for doing it with the WKWebViewConfiguration class as well?Tazza
W
2

If you need to support iOS7, you cannot use WebKit2 (WK*) classes, or you need to implement twice the logic, once for iOS8 using WK* and once using UIWeb*, and at runtime choose according to the operating system version.

Wanting answered 16/8, 2014 at 14:56 Comment(1)
That sentence 2 about "Make sure you weak link WebKit.framework (set to optional)" is super important. I actually didn't even have WebKit.Framework listed so I needed to added it and set to Optional.Lubberly

© 2022 - 2024 — McMap. All rights reserved.