how to write a minimal webkit browser in 37 lines of swift

UPDATE 27 October 2017: This post has been broken for quite some time, ever since Swift 3 was released. I’ve tried to use the Swift tools and capabilities to update it, but I’ve never succeeded and I’ve never taken the time to figure out the peculiarities. Furthermore the site practicalswift is gone, so that link has been removed.

#!/usr/bin/env swiftimport WebKitlet application = NSApplication.sharedApplication()application.setActivationPolicy(NSApplicationActivationPolicy.Regular)let window = NSWindow(contentRect: NSMakeRect(0, 0, 800, 600), styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask, backing: .Buffered, defer: false)window.center()window.title = "Minimal Swift WebKit Browser"window.makeKeyAndOrderFront(window)class WindowDelegate: NSObject, NSWindowDelegate {func windowWillClose(notification: NSNotification?) {NSApplication.sharedApplication().terminate(0)}}let windowDelegate = WindowDelegate()window.delegate = windowDelegateclass ApplicationDelegate: NSObject, NSApplicationDelegate {var _window: NSWindowinit(window: NSWindow) {self._window = window}func applicationDidFinishLaunching(notification: NSNotification?) {let webView = WebView(frame: self._window.contentView.frame)self._window.contentView.addSubview(webView)var url = NSURL(string: "https://www.google.com")webView.mainFrame.loadRequest(NSURLRequest(URL: url!))}}let applicationDelegate = ApplicationDelegate(window: window)application.delegate = applicationDelegateapplication.activateIgnoringOtherApps(true)application.run()

This is little more than a copy of the code published on practicalswift, “How to write a minimal WebKit browser in 30 lines of Swift.” I made several trivial changes to the code so that it would properly execute in my Xcode environment.

  1. The shebang was cleaned up.
  2. Line 30 used to be one line (see original code here). In order to get it to work I removed the NSURL instantiation that was chained in webView.mainFrame.loadRequest and created the separate variable url right above it on line 29. I then added a “!” right after url. I’d tried to add the “!” while it was still one line, but Swift wasn’t having any of that, so I split them apart. I’m certainly no Swift expert, but long years of battling other oddball errors in other languages has trained me to begin to break apart compound function calls made with instantiations into more, but simpler, lines, if for no other reason than to isolate the problem as an aid to debugging. In this instance, just breaking them apart made the whole thing begin to work (see application capture above).

There’s a lot for me to learn in these 30-something lines, and a lot of functionality to add. This surely won’t compete with the likes of Safari or Chrome. But it does show how complete WebKit is. I can go to any website via the Google search box and see it properly rendered. Quit an nifty feature when you don’t have to write your own rendering engine.

Also, this particular block of Swift scripting is bound to the Mac and OS X runtime environment. Ain’t no way this is going to be portable to anything else other than a Mac running OS X.

swift 101 – scripting with swift

I’ve spent parts of Saturday and today going over Apple’s “The Swift Programming Language,” looking to get a toe-hold on how to use Swift. Using vi and some of the examples in the Swift book, I hacked together a few scripts that illustrate some of the features of the language. I also went out on the web to find a few features I couldn’t find reading the book.

This first example, reading command line arguments, came from the web. I couldn’t find an example in the book (but watch me find it later). Every language, scripting or otherwise, provides a framework for reading any arbitrary number of arguments passed to the application when the application is invoked. What follows is at least one way to read command line arguments in Swift, and what the output looks like when it executes

#!/usr/bin/env swiftprintln("Process.arguments gave args:")for arg in Process.arguments {println(arg)}

Comments about the code and the output:

  • The shebang line (line 1) is standard for all scripting languages. I’ve seen more elaborate shebang lines on other postings, but this one, which is “classic” Unix/Linux, works just fine on Yosemite OS X 10.10.1.
  • The Process class (line 5) is unique to Swift.
  • In the output, the first argument (usually considered the zeroeth argument in other languages like C and C++) is the name of the script/program that was invoked. This will include the full path.
  • Process.arguments can handle any arbitrary number of command line arguments.

I first came across this on the practicalswift.com website, and it had a more elaborate shebang line (thus the comments above). But the site’s version was written back in June 2014, during the beta period. It’s a bit sad that nothing new has been written on the site since June 2014.

This second example shows a big of class declaration and class inheritance within Swift. This is all written in the same script; I’ve yet to learn how to create separate script/compilation units to be included by a main executable function.

#!/usr/bin/env swiftclass NamedShape {var numberOfSides: Int = 0var name: Stringinit(name: String, numberOfSides: Int) {self.name = nameself.numberOfSides = numberOfSides}func simpleDescription() -> String {return "A \(name) with \(numberOfSides) sides."}}var nshape = NamedShape(name: "square", numberOfSides: 4)println(nshape.simpleDescription())class Square: NamedShape {var sideLength: Doubleinit(sideLength: Double, name: String) {self.sideLength = sideLengthsuper.init(name: name, numberOfSides: 4)}func area() -> Double {return sideLength * sideLength}override func simpleDescription() -> String {return "A square with sides of length \(sideLength)."}}let square = Square(sideLength: 5.2, name: "my test square")println(square.simpleDescription())println("The area is \(square.area()).")

Comments about the code and the output:

  • This is modified code from pages 24 and 26 of “The Swift Programming Language.” I added NamedShape’s init() function and changed NamedSquare’s init() accordingly. I’m a firm believer in constructor initialization, no matter what.
  • Both classes’ have public members. Not shown here, I wrote code that accessed both classes attributes. I don’t know how to make class attributes non-public.
  • Both classes have the equivalent of constructors (the init function).
  • When instantiated you must declare the argument type by name (see lines 17, 25, and 37). I was surprised by this at first, but whatever, it’s one of the rules, and when in Swiftville, you do what the Swifties do.
  • Class Square shows inheritance from NamedShape (line 20) as well as over  riding Square’s simpleDescription() at line 32.

There are other scripts I’ve written, such as a largish one investigating the use of dictionaries (associative arrays). The syntax is clean and easy to read, allowing for some fairly sophisticated data structures.

There’s a lot more I need to learn, the most important being how to easily set up file I/O. Other facts I’d like to learn about Swift is how to spawn applications, script interop communications, some possible threading, and how to link to other Swift scripts/classes. But it’s late and the Labs are staring at me for their walks. Time to sign off.