Over the last few weeks, my project to turn LogLogger’s AppleScript into a Swift ‘script’ app has become progressively more complex. I still think that it is, basically, a script app, as all the code is written into the View Controller, and basically handles its dialog-based interface.
There comes a point, though, when you have to dive deeper. One of Consolation’s biggest problems is its single window; this is great for a quick check of Time Machine backups, but when you are rummaging around in scheduling and startup routines, it get severely limiting. The next step, therefore, was to build support for the user to open multiple windows.
Starting from a basic Cocoa app written in Swift 3, there are three possible routes to support multiple windows.
The most direct would be to devise a method of doing so without progressing to a full document-based app. I am sure that is possible, but everywhere that I looked suggested that it was largely uncharted territory, if not tiger country. I could be lost for weeks, even eaten alive by the problems.
The next would be to convert the app into a document-based app in situ, by adding the classes necessary. This has been accomplished by some developers, although the one account I found seemed to involve a team of several experienced Cocoa developers working for several weeks. The solution was not documented, so that looked as hazardous as the first route.
The last was to start from scratch by creating a new project in Xcode, to produce a document-based Cocoa app, and then try to paste in as much as I could from Consolation version 1. I was unable to find any instructions as to how to move my Storyboard across, but just had to hope that I wouldn’t end up having to implement the controls from scratch too.
Once you have got an app working properly, it is very frustrating to have to start again, and I was a little bitter that Apple has fixed these two different types of project, with no way of conversion from one to another. That perhaps reflects the difficulties of such conversions, though.
So I created my new project, making it a Cocoa Application for macOS, using Xcode.
The choice to create a document-based app does not appear until the options, and is tucked well away in a simple checkbox. When you choose it, you also need to specify the main file extension to be used by its documents, in this case plain .text.
Many of the settings and properties differ for a document-based app, and you need to check that the document type(s) are set correctly here too.
Once the new project was set up and ready to edit, I set myself three tasks:
- To copy across as much from the old app’s View Controller as possible, so that I would not have to re-implement much of its interface.
- To connect that up to identical Outlets and Actions in the View Controller source. I could see no way of pasting those connections, and resigned myself to having to drag each one from the view to the source file.
- To copy across all the other code. If I used exactly the same names for the Outlets and Actions, this should work without modification, I hoped.
The first task proved much easier than I had expected. I manually set the window and view to the same sizes and properties as in Consolation 1, then in its source, I selected the View, copied it from there, and pasted it into the new View Controller. As the links did not travel with it, all the controls had different names, but otherwise settings – even tags for radio buttons within a group – moved across perfectly.
I then worked down the list of items in the view, which was in the same order as in Consolation 1, and created all the Outlets and Actions, giving them the same names as in the original. This was the most time-consuming step, but was not the nightmare that I had feared it might have become.
My new ViewController.swift source file then looked identical to the original, with each of its functions empty. I finally copied and pasted their contents, leaving that file identical to the original, with each of the functions correctly connected.
After only about ninety minutes, I was ready for a trial build, to see whether the resulting app ran. At this stage, I had not touched any of the additional source files required to support documents, etc. To my amazement, the app built fine, and worked just as before, with multiple window support.
The first beta was not yet ready, though. I had two more important tasks to complete before I could start using this beta, let alone unleash it on the public.
The more tricky of those was to implement the File / Save… command. Consolation 1 handled this through a button, but with the full document-based architecture I really had to do the job properly, and write the required code into the data(ofType typeName: String)
method in Document.swift. After a bit of scurrying around on the internet, in my iBooks library, and in Apple’s antiquated (and Objective-C-based) documentation, I came up with the following code:
override func data(ofType typeName: String) throws -> Data {
if let theVC = self.windowControllers[0].contentViewController as? ViewController {
let fileContentToWrite = theVC.theResTxt.string! + "\nAt " + theVC.theNowStr + " for command log " + theVC.theFullCmdStr.joined(separator: " ")
return fileContentToWrite.data(using: String.Encoding.utf8) ?? Data()
}
else {
return Data()
}
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
Xcode kindly informs me that final throw
is never called, but I left it in for the sake of completeness, recognising that I need to revisit this and write in proper error-handling in the near future.
Once again, this built first time, and appears to work perfectly.
There are other areas which still need careful attention, notably printing. Consolation 2 will also never be capable of opening any existing document, so the second and final job to prepare the first beta-release was to disable or remove menu items which are currently unused.
The job that I was dreading had taken me just one afternoon. Consolation 2 is hugely improved with its new support for multiple windows, and the first beta feels robust and at least as fully-functional as version 1.0. For once, Xcode proved its worth, and made a very complex job straightforward and thoroughly reliable.
At the end of all this, I think that this app has now passed from simple scripting to proper app development – something that was never easy with AppleScript.