The Slow Shift to Swift: False Starts and Frustrations

Jul 1, 2015

This has as much to do with the Mastery Curve as anything.

Master curve

It took a very short time to pick up Objective-C to the point that Time Journal could be released to the App Store. The verbosity of the language was a welcomed relief for me as I tend to write code in a self-documenting fashion, which is to say, a developer (namely, future me) can come in with limited prior knowledge and have an understanding of the system.

With Objective-C, the APIs are written in such a way that limited commenting of the code is necessary to describe what is going on:

-(UITableViewCell*)tableView(UITableView*)tableView cellForRowAtIndexPath(NSIndexPath*)indexPath {
    // do something
}

If you know the syntax of Objective-C the above is easy to read and understand. We will be returning a pointer to an instance of UITableViewCell. In order to do that we need to know the instance of UITableView to act on, and what instance of NSIndexPath is being referred to.

Compare the above to something like the following:

-(c*)t(t*)t ip(ip*)ip {
    // do something
}

Now you need to gain a better understanding of the system as a whole before continuing. What does c mean? What about all the t references? And what does intellectual property (ip) have to do with working software?

Some developers would overcome the problem by adding comments to their code:

/// :return: c An instance of UITableViewCell
/// :param: t An instance of UITableView
/// :param: ip An instance of NSINdexPath
-(c*)t(t*)t ip(ip*)ip {
    // do something
}

So, in an effort to reduce the number of characters required to type out the method name, we need to add even more characters in order to explain it. And, if you ask the developer who put this together, sometimes the first words you’ll hear are: Just look at the code, it’s pretty self-explanatory.”

This is where having a bent for UX, as a general concept and not a specific application, comes in handy; because the response is usually more along the lines of: What’s making it difficult? maybe it needs to be changed.

In the beginning, all I really wanted to do was take the Time Journal code and change it to the Swift syntax, without taking advantage of what Swift brings to the table.

The First False Start

Swift doesn’t really want to act like Objective-C. In Objective-C you can typically pass around an object representing nothing and not have to worry too much about it. Unfortunately, this is what I would consider a code smell in both Objective-C and Swift; however, Swift is way more in your face about it.

In Swift, there is no concept of a nil pointer (something that points to something that represents nothing).

// indexPath is a pointer to nothing
NSIndexPath *indexPath;

// succeeds and i is 0
NSInteger i = indexPath.item;

// check if indexPath is a pointer to nothing
if (indexPath == nil) {
    // mutate indexPath to be something
    indexPath = [NSIndexPath indexPathForItem:1 inSection:0];

    // succeeds and j is 1
    NSInteger j = indexPath.item;
}

In both of the succeeds, by having knowledge of the Objective-C environment you can see how it succeeds. But, understand we just asked nothing (shouting to the heavens), for a value that shouldn’t exist, and got back a value. Compared to the second example, where we instantiated the NSIndexPath and set a value.

In Swift, on the other hand, nil is nothing and a part of the syntax.

// indexPath is a pointer to nothing
var indexPath: NSIndexPath?

// fails - cannot compile
// let i = indexPath.item

// fails - unwrapping the indexPath with the !
//         results in a runtime error (application crash)
// let j = indexPath!.item

// check if indexPath is nothing
// assign return value to immutable
if let iPath = indexPath {
    // we never get here, because indexPath is nil
    indexPath = NSIndexPath(forItem: 1, inSection: 0)

    // succeeds - because indexPath is not nil 
    //            unwrap succeeds and j is 1
    let j = indexPath!.item
}

And, just in case you find the above difficult to parse, here it is without the cruft:

var indexPath: NSIndexPath?

if let iPath = indexPath {
    // we never get here
    indexPath = NSIndexPath(forItem: 1, inSection: 0)

    let j = indexPath!.item
}

So, Swift forces you to make better architecture decisions by stopping from messaging nil. In the beginning my first response was to hack my way through—put exclamation marks (!) everywhere! And, I mean everywhere!

But, then it hit. That moment when your mind figures it out: Stop fighting and change the code! And, luckily, with Swift forcing you to change the Objective-C implementation we can see it was just poorly designed from the start because we can now write it like this.

let indexPath = NSIndexPath(forItem: 1, inSection: 0)
let j = indexPath.item

That’s all you need.

So, give up the old architecture. Got it!

But, that was only half the battle.

False Start Two

Core Data. Core Data? Core Data!

One of the technologies that actually got me interested in developing in Objective-C and on the Apple platforms was Core Data (their memory storage and persistence framework). Basically, it felt very similar to something I had been writing in PHP for a while to help eliminate defects when interacting with databases. Core Data is used in Time Journal pretty extensively. Unfortunately, there are some issues in Swift when it comes to Core Data objects—which I won’t go into.

This false start, admittedly had me so frustrated I decided to put converting Time Journal away for a while.

So, I spent a month or so putting my brain back into Objective-C. Thinking about the next phase of 8fold Productivity. The web and what new things were happening there. Had the privilege of attending an Agile Bootcamp held by the Agile Coaching Institute with Lyssa Adkins, Michael K. Spayd, and Michael Hamman. And a little family emergency…

I rode the plateau. I rode it well. All anxiety and frustration about Swift, Time Journal, and Swift and Time Journal melted away.

False Start n

The rest of the false starts sort of mirrored the first two. But, Apple has been pretty quick to update Swift and Xcode to help adoption. And, some of the false starts had nothing to do with Swift or Xcode, they were moments of learning for me and the new technologies Apple had released and updating the structure of the project based on new knowledge.

I’m in the midst of a start again. It is going smoother this time. My automated tests are passing with each incremental change. I’m teasing out reusable code for future development; to minimize the potential time debt of switching over.

Ash Furrow did a good job describing the angst of trying to use Swift in production right now. He also did a nice write-up on the things Swift allows for from an architecture and design perspective.

I can feel my brain kicking over further toward where it was wanting to go all along: Software is math—1 + 1 should never be 3 when passed around; or, as Ash said, “In functional programming, we try and avoid mutable data and state.”

###