Swift Baby Steps: BLAKE.app
I'm a long-time Mac OS X developer, going back to the Mac OS X 10.1 Puma fourteen years ago. While my earliest app was a book-cataloging app (R.I.P. Books.app), I've continued to develop for the platform, with some of my latest projects being internal tools to assist Fresh Comics weekly data updates. However, during that period, I remained a staunch Objective-C developer, operating under the theory that I didn't need to jump into Swift until the language progressed to a point that I was being left behind feature-wise with Objective-C. However, given some iOS projects in the pipeline, I decided to give the language a try this evening. I've recorded some thoughts below.
This evening's exercise had three main goals:
- Write a small Mac app using Xcode in a manner that incorporates development tasks I've avoided since I stopped releasing consumer Mac products. This includes new technologies like storyboards, developer code signing, and the latest Xcode deployment processes.
- Write the app in Swift to begin wrapping my head around the language, its conventions, and its pros and cons.
- Implement functionality that reinforces the work management strategies I've been developing with my BLAKE system.
Over the past month, the one activity that I use BLAKE most frequently for is tracking my progress working billing hours in allocated time blocks I've sold clients. This typically involves me keeping my dashboard page open and refreshing the Active Time Blocks to see where my time's been spent and where it should go next:
Given that this graph is what I look at the most, the first feature of the new BLAKE.app would be to make that information available in a more convenient fashion. I decided to build a Mac app with a minimal visual footprint that resided in the system's menu bar and status would be available by tapping the app's icon. The initial version of the app looks like this:
Currently, it displays the active time blocks and indicates whether I'm on schedule (green icon) or behind schedule (red icon). Any of the menu items may be clicked to open up the web page containing the list of current tasks assigned to that time block. The app refreshes its status every five minutes, but the Refresh Blocks… item can be clicked to force a manual update.
Beneath the Active Time Blocks section are items that open frequently used elements of the BLAKE system. Open Dashboard… opens the overall dashboard page, while Open Tickets… launches the master tasks list. Open Time Tracking… launches the page that allows me to log hours spent working.
This initial version of the app relies heavily on web shortcuts for accessing BLAKE's features. In the future, the app will be extended to implement some of these features natively:
- Improving the ease of logging time and new tickets by providing native interfaces to record that information from within BLAKE.app.
- Tracking weekly overall time spent. (e.g. Am I on track to log 40 billable hours this week?)
- Searching and filtering tickets from a native interface that's faster than the Django administration interface I'm currently using.
- Integrating service-issue notifications from Nagios into the local interface (and banishing them from my e-mail).
I'll be writing new posts as these features are added.
With the application context out of the way, let's discuss development in Swift. First of all, I didn't get too heavily into class structures in this project as I was able to implement all of the initial functionality in the AppDelegate class. (I'm looking forward to giving its limited namespaces a spin.) The
applicationDidFinishLaunching delegate method implements the app's menu by programmatically creating the app's menu by creating and configuring
NSMenuItem instances in Swift code. I did not use a XIB or storyboard file to implement any user interface (yet). The delegate launch method ends by creating a
NSTimer instance set to fire every five minutes to refresh the menu items.
The remainder of the bulk of the implementation resides in a
refreshActiveTimeBlocks method that issues a request to the BLAKE server and parses the received JSON document to build out the Active Time Blocks section of the app's menu. I included some basic error handling that will update the app menu icon to reflect when the app's been unable to retrieve the requested information.
This was a sufficient exercise that allowed me to get my feet wet with some Swift basics. Some initial reactions:
- I don't feel like writing this app in Swift was an improvement over Objective-C productivity-wise. I ended up with the same number of lines of code as I would have generated in Obj-C and the lines are pretty much direct Swift translations of Objective-C calls to the Cocoa framework.
- I like the
letkeywords that let me express my intent if a given object is a variable or constant.
letreminds me strongly of the
finalkeyword in Java.
- It feels very strange to me to have C-style code blocks without semicolon statement terminators.
- One of the common complaints about Objective-C is the proliferation of square braces. In Swift, there seemed to be a similar proliferation of
!operators in the code. While I'm glad that I had Xcode to help me figure out where those went, it seems like a lot of code pollution for dealing with
nil-able values when I still have to check if the wrapped values are
nilin my code.
- When using labeled method arguments, Xcode was insistent that I not label the first argument in the list as per Swift convention. I don't entirely understand the reason behind that convention. If I'm labeling the second argument and beyond, consistency should dictate that I label the first argument as well.
- Swift seems okay with type declarations being omitted, but I wasn't entirely sure if this was roughly equivalent to declaring
idin Objective-C. In my code, I declared types with the variables, but this doesn't seem to be in-line with the convention. Personally, I prefer declaring the types up front and not dealing with all the
(variable as Type)verbosity later in the code.
- Swift likes to masquerade as a dynamic language, but I was disappointed that there apparently are not more powerful mechanisms for dynamically creating closures in the context of the Cocoa API. In my particular case, when BLAKE.app fetches the Active Time Block information, I wanted to pass a closure along to the newly created
NSMenuIteminstance that would open the block's ticket URL in the default web browser. The expected behavior in a Swift-like language would be to pass a closure object to a method on
NSMenuItem. However, I had to implement that functionality using the exact same target/selector mechanism I would use in Objective-C. I recognize that this may be the fault of the Cocoa API (no such method on the Cocoa object), but this seems like a missed opportunity to make the language shine.
I want to close out this post with a disclaimer that the reactions above are from an old Objective-C coder wading into Swift, and as such, the Tao of Swift may not yet have been achieved (extremely likely). Over the next month, I'll be diving into the user interface side of things as I begin to pull interfaces exclusive to the BLAKE web server into the local BLAKE.app and I'll be documenting my Swift education accordingly.