Writing our first Swift app
/Just released Yearn (shameless plug time: app store link, or mini-site), which has been entirely developed in Swift, and I wanted to do something green-field in Swift (before looking at a transition plan for other apps if Swift proved to be mature enough), and this was an app idea that had been bubbling away for a while. Swift and iCloud Photos beta made it feel like the timing may be right.
TLDR? Swift itself was an real win, although the tools have some catching up to do.
The Language
Swift had two clear design goals (at least, two that really resonated with me): Be concise, be robust. In the first, I really feel Swift excels. Even simple things like dropping semi-colons clears a lot of useless noise from your screen, add in other small details like removal of brackets for conditions and I felt that my code was and remained highly readable.
I think the second will be tested as the App gets used on a broader range of devices, but I can comment on beta testing results (for which Test Flight performed admirably).
After writing about the language features of Swift, I settled on a list of rules that I would follow. The one I felt was most important was that I would avoid forced unwrapping at all costs. I think this is key. There are other language features (requiring forced fall-through for switch statements, generics, forcing braced if's) but making you think about how nil can propagate through your code drives some important behaviours, it must be handled. Of course you can force unwrap with the ! operator, but as stated I decided that this would be a real no-no. However, there are those that argue that this drives clutter into the language, directly in opposition to the goal of concise and clear code. It's worth looking at a few common patterns used to deal with optionals.
The first concerns methods or variables that may be nil. Swift makes it very easy to deal with these with unwrapping. If we have a function
func myFunc()->MyObject?
We can still chain other methods calls against the returned MyObject?
let result = myFunc()?.objectMethod()
Of course, result is now optional too, but as we know Swift has another language short-cut to make that situation easy to deal with
if let result = myFunc()?.objectMethod()?.anotherValue?.serverResponse { //Everything went well } else { //Handle the problem }
The same pattern can be employed on longer method chains, but of course each step obfuscates the root cause of the issue; which call returned nil? If you want to respond intelligently, or report a useful actionable message to the user, We found a useful pattern to deal with this using switch.
//Break out my chained calls, not worrying about optionals on the way let myFuncResult = myFunc() let anotherValue = myFuncResult?.anotherValue let serverResponse = anotherValue?.serverResponse //Deal with any failure in the chain switch (myFuncResult,anotherValue,serverResponse){ case (nil,_,_): println("Couldn't get my object") case (nil,nil,_): println("No value available") case (nil,nil,nil): println("Couldn't get response string from response tuple") default: println("Server responded \(serverResponse!)") }
Of course this is a trivial example that leaves more boiler plate than code, but I'm sure you can see the strategy here. Stop at each point in the chain where nil would be indicative of a actionable or communicable state, and then at the point where you are either done or in trouble have a single chunk of code that will either move forward with a usable result or respond to a problem.
The key thing here is that the application logic is left unadulterated by risk from nil, but the error handling is able to use information about the chain.
Otherwise Swift was a pleasure to use. We didn't start until close to the GM, and by that time Swift churn had calmed down enough to mean we had little if anything to do in each subsequent release. The language feels ready for prime time, with perhaps one plank in its eye.
The tools
I'm not going to spend too long here, if you you've XCode with Swift you already know what I'm going to say
It doesn't get really annoying until that has been flickering up a boing-ing message so much that the main thread is blocked. By the time XCode 6.1.1 had rolled around almost everything else that felt it was getting in the way had been fixed, but this really breaks your flow as you work. So work still to be done editing code... what comes after editing... oh yes compilation
Whilst the situation improved during the various beta's, there were (and are) two major bugs that caused me to change the structure of our code (both around protocols and class/static variables with generics). I'm sure they will be solved, but these issues need to be at the top of Apple's list.
So you've edited (boing boing boing boing boing, beach ball, back), compiled (1 error, segfault in the compiler, refactor to carry on), which just leaves debugging... To be fair to Swift the aggressive avoidance of forced unwrapping meant that we only needed the debugger for functional problems rather than crashes. However, all to often the debugger is unable to tell me what is in self, let alone things that might live a little further out of scope. One can work around this (although I can't add po's to the breakpoints as they deliver the same result) with println's and similar techniques, but it's frustrating and difficult at times.
So you know, apart from editing, compiling and debugging the tools are in great shape. I'm not an idiot, and I am deeply impressed that Chris and the rest of the Apple team have come this far in such a short space of time since beta 1, but there is still a way to go and to ignore that would be fool-hardy.
Conclusions
Overall, it was a pleasure. Of course I've need to work in other languages during the period, and Swift has managed to make all of them (strangely with the exception of JavaScript) feel heavy and cluttered (especially Java). The tooling issues are an annoyance, and they have cost me hours of time. However, overall my feeling is that it has taken me less time, not more, to develop the app, and that it is more stable as a result. I have been able to focus on the functionality, when I have had to spend time on other projects it has been easier to settle back in to Yearn, Swift leaves your logic exposed and readable.
That has to be a major win just 6 (ish) months after the release of a brand new language. Here's to the future.