WKWebView via Interface Builder, runs in Xcode but not in built macOS app

I've been attempting to make a simple, test macOS app using WKWebView. I've seen lots of samples and basic tutorials, and this rudimentary sample code works when tested directly in the XCode environment:


import Cocoa
import WebKit

class ViewController: NSViewController {

    @IBOutlet weak var webView: WKWebView!
   
    override func viewDidLoad() {
        super.viewDidLoad()
       
        var myURL: URL!
        myURL = URL(string: "https://developer.apple.com/")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)

    }

}


When it runs, I see an app window with an embedded WKWebView frame, and the specified URL loads within that frame as expected.


However, when I attempt to run the built app file directly on my iMac, all I get is an empty window. I don't even see the frame for the WKWebView object.


The WKWebView was placed in the main.storyboard and the outlet was linked through the standard ctrl-drag operation. The entitlements for App Sandboxing and Network Outgoing Connections are set (otherwise, the URL won't load in Xcode, either).


So, my question is -- should this work? I've seen comments around the web that one cannot implement WKWebView via Interface Builder in older versions of Xcode/macOS, but my tests show it currently works within Xcode 10.2 on macOS 10.14.4. Why would it work in Xcode but not in the built app itself? Am I missing something?

Accepted Reply

You should file a bug report. I guess (but can only guess here) there are still some problems with WKView created in IB.


At least you may get informed feedback.


Good luck.

Replies

I've just tested and it works, both in XCode and when option-dragging the app outside.


Conf: XCode 10.1ß3

Storyboard, no sandbowing

OSX 10.13.6


But I did not use an IBOutlet for webView, which causes an error

Swift : error: Class Unavailable: WKWebView before macOS 10.12 (NSCoding support was broken in previous versions)

see:

// https://stackoverflow.com/questions/46221577/xcode-9-gm-wkwebview-nscoding-support-was-broken-in-previous-versions


So, that should work with 10.13, but visibly, it does not…


So, my running code is:

import Cocoa
import WebKit

class WebViewController: NSViewController, WKUIDelegate {
  
    var webView: WKWebView!
  
    override func loadView() {
        super.loadView()
        let webConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 400, height: 400), configuration: webConfiguration)
        webView.uiDelegate = self
        view = webView
    }
  
    override func viewDidLoad() {
        super.viewDidLoad()
      
        var myURL: URL!
        myURL = URL(string: "https://developer.apple.com/")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
      
    }
  
}



UPDATE


Just tested (same environment) with sandbox activated. DOES work also.


So, my guess:


don't define webView in IB, but directly in code as shown above.

May be there are some remains of the previous bug…

Indeed, I've also tested the same code for creating the WKWebView programmatically. That method works for me both within Xcode and the built app as well.


However, my reason for wanting to use Interface Builder is to:

  • easily define the size and resizing of the WKWebView within the window -- I don't want it to fill the entire window
  • add additional controls and buttons above the WKWebView


So, while the programmatic method works, it takes up the whole window, and I'm struggling to get it to position and resize the way I need via code. It also negates the benefit of using Interface Builder to create the storyboard visually, which is what Apple recommends for adding the layout constraints.


Note that in Xcode 10.12.1 and building for a macOS 10.14 target, the NSCoding error doesn't occur. That's what has me wondering if the IBOutlet approach:

  • should work, but I've missed some step, entitlement, or other configuration to prevent the built app from working
  • should work, but there's a bug in Xcode
  • shouldn't work, but for some reason there is no error report within Xcode

You should file a bug report. I guess (but can only guess here) there are still some problems with WKView created in IB.


At least you may get informed feedback.


Good luck.

Thanks, I've filed a bug report. [ EDIT: it's number 50272736 ]

Just for completeness, could you post the bug number ? And don't forget to close the thread.

This appears to be a well-known problem since at least 2015: www.openradar.me/23699297

If you change


        webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 400, height: 400), configuration: webConfiguration)


to something like


     let width = // window.width - 40
     let height = // window.height - 80

     webView = WKWebView(frame: CGRect(x: 20, y: 20, width: width, height: height), configuration: webConfiguration)


Then, it should not use the full window but leave a 20 margin on left and right and top and 60 at bottom.

It just seems odd that it's working within Xcode without any error messages. If it didn't work at all, then it would make more sense.

I tried it but I was unable to get an IB WKWebView to work in any context. In my app, I can creating web views as needed and expanding them to fill their container.

That doesn't seem to work. Even just setting x: 20, y: 20, there is no gap from the corner; it still fills the window.

Any tips on setting the sizing? So far I can only get the WKWebView to fill the entire window, no matter what CGRect values I use. I need to push the top of the view down to make room for some controls, then it should otherwise fill and resize with the rest of the window on the left, right and bottom.

Eureka! I found the solution.


In order to make WKWebView resizable, I needed to added it as a subview. This test code is effectively what I was after:


import Cocoa
import WebKit

class ViewController: NSViewController, WKUIDelegate {
   
    var webView: WKWebView!
   
    override func viewDidLoad() {
        super.viewDidLoad()

        let wvHeight = self.view.frame.height - 40
        self.webView = WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: wvHeight))

        self.webView.autoresizingMask = [.width, .height]
        self.view.addSubview(self.webView)

        let myURL = URL(string:"https://developer.apple.com")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
    }
   
}

I put this method in an NSView cateory:


// Expand this view to fit its superview.
- (void) expandToFit
  {
  NSLayoutConstraint * leading = 
    [NSLayoutConstraint 
      constraintWithItem: self 
      attribute: NSLayoutAttributeLeading 
      relatedBy: NSLayoutRelationEqual 
      toItem: self.superview 
      attribute: NSLayoutAttributeLeading 
      multiplier: 1.0 
      constant: 0.0];
       
  NSLayoutConstraint * trailing =
    [NSLayoutConstraint 
      constraintWithItem: self 
      attribute: NSLayoutAttributeTrailing
      relatedBy: NSLayoutRelationEqual 
      toItem: self.superview 
      attribute: NSLayoutAttributeTrailing 
      multiplier: 1.0 
      constant: 0.0];
    
  NSLayoutConstraint * top = 
    [NSLayoutConstraint 
      constraintWithItem: self 
      attribute: NSLayoutAttributeTop
      relatedBy: NSLayoutRelationEqual 
      toItem: self.superview 
      attribute: NSLayoutAttributeTop
      multiplier: 1.0 
      constant: 0.0];

  NSLayoutConstraint * bottom = 
    [NSLayoutConstraint 
      constraintWithItem: self 
      attribute: NSLayoutAttributeBottom
      relatedBy: NSLayoutRelationEqual 
      toItem: self.superview 
      attribute: NSLayoutAttributeBottom
      multiplier: 1.0 
      constant: 0.0];
    
  [self.superview addConstraint: leading];
  [self.superview addConstraint: trailing];
  [self.superview addConstraint: top];
  [self.superview addConstraint: bottom];
  }


It's Objective-C, but should be trivial to convert to Swift. I just add the WKWebView. I set an onLoad completion handler and then start the view loading. When the loading completes, I add the web view to a superview with animation and call expandToFit. I position the superview in IB like normal. Make sure to call setTranslatesAutoresizingMaskIntoConstraints: NO after creating the web view.

Hi there,

I had the same problem. Seems to be some bug in Xcode / Interface Builder. I was able to solve the issue by explicitly linking the WebKit framework to my project.
In XCode, go to your target -> "Build Phases" -> "Link Binary with Libraries" and explicitly add WebKit.framework. From thereon, everything works as expected :-)

Hope that helps.