Putting a browser window in your app

LockRattler checks the currently installed versions of several security data files. As users have rightly pointed out, being told that you have version 2099 is only part of what you need to know. Without being able to check what that number should be, the information is of limited value.

My task then was to incorporate ready access to the latest version information which I publish on this blog, in a series of three separate articles, covering El Capitan, Sierra, and High Sierra.

The ideal solution would have been to obtain the version information via a REST request. I could then format the response, set into JSON, and compare the versions expected with those found. Unfortunately, for the moment that is not easy to do from WordPress blog pages. WordPress has a REST interface coming along, but I think it will be a while before it is completed, reliable, and fully documented.

A quick kludge would simply be to open the appropriate page in the default web browser. Although better than nothing, I thought that I could do better than that.

In principle, it should be straightforward to use WebKit in a simple browser window, and to point it at a particular URL. This used to be done through the WebView class: open a window with a WebView view in it, and ask it to open the URL. Apple’s current documentation refers to that as a legacy approach, though, stating:
In apps that run in OS X 10.10 and later, use the WKWebView class instead of using WebView.

Furthermore, Apple says in the overview for WKWebView:
Important
Starting in iOS 8.0 and OS X 10.10, use WKWebView to add web content to your app. Do not use UIWebView or WebView.

That may be true, but when I had implemented that in LockRattler, which runs on El Capitan and later, Xcode decided that I couldn’t use WKWebView because of implementation bugs. It only works in Sierra and later, not in El Capitan. So, assuming that you want to cater for 10.11 too, here’s how to do it.

In Interface Builder, create the window which is to contain the WebView, and add to its view a Web View (legacy), rather than a regular Web View which is intended for use with WKWebView. Make a custom NSWindow class to use for the window, to avoid Xcode complaining about having to use the default, and create a custom NSViewController into which your browser code will be placed.

Because this is a separate window, I control it from the App Delegate, which contains a list of those browser windows
var myBrowserWindow: [NSWindowController?] = []

Opening a new browser window from there is simple, provided that you remember to give the Window Controller a Storyboard ID:
func openBrowserWindow() {
let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
let theBrowserWindow = (storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Browser Window Controller")) as! NSWindowController)
theBrowserWindow.showWindow(self)
self.myBrowserWindow.append(theBrowserWindow)
}

lockrattler4205

The button to open this browser window then needs to access the App Delegate and call that function:
@IBAction func checkBlogButton(_ sender: Any) {
let delegate = NSApplication.shared.delegate as! AppDelegate
delegate.openBrowserWindow()
}

lockrattler4206

In LockRattler, the page to be opened is determined by the version of macOS which is running. This makes the ViewController a little more complex (!).

First, we have to import WebKit as well as Cocoa, and wire up the Web View (legacy) view as an outlet:
import Cocoa
import WebKit
class WVViewController: NSViewController {
@IBOutlet var webView: WebView!

It then takes a single function to create the URLRequest, and call for it to be loaded:
override func viewDidLoad() {
super.viewDidLoad()
var myURL: URL!
let osVersion = ProcessInfo.processInfo.operatingSystemVersion.minorVersion
if (osVersion > 12) {
myURL = URL(string: "https://eclecticlight.co/lockrattler-10-13-high-sierra/")
} else if (osVersion == 12) {
myURL = URL(string: "https://eclecticlight.co/lockrattler-10-12-sierra/")
} else {
myURL = URL(string: "https://eclecticlight.co/lockrattler-10-11-el-capitan/")
}
let myRequest = URLRequest(url: myURL!)
webView.mainFrame.load(myRequest)
}

lockrattler4207

If you’re able to use a WKWebView, all that really differs in terms of code is the last call of that function, which would then be
webView.load(myRequest)
instead, acting on a WKWebView rather than an old WebView.

That is all there is to it, although that browser window is minimalist and lacks all controls, of course.