The Slow Shift to Swift: ...why is it there?

Jul 22, 2015

The full title should really be: If it’s optional, why is it there?

As a productivity coach, and pretty hardcore minimalist, I tend to question the existence of things we call optional. When it comes to continuous improvement and iterative design I’m always looking for ways to reduce things to the essence of what they are. Put another way, if a sentence can communicate the same idea without a word, I remove it. Further, if a piece of code can perform the same function without a variable, I remove it. Having said that, sometimes you can’t see where this distillation can occur because of the noise and distractions. This signal-to-noise problem is particularly true in most programming languages, which depend on syntactic sugar in the form of symbols to help the computer know what we are trying to communicate—Swift eliminates much of this syntactic sugar; however, when it comes to nil and optionals, they are a mixed blessing.

The ability to have nil, and the various methods for creating private variables and syntactic sugar in Objective-C makes distilling code difficult sometimes. This concept became pretty apparent as I refactored the extension for NSManagedObjectContext in Time Journal.

We have a data handler that sorts and sanitizes elements used for the creation of the clock list. The data handler retrieves this information from methods on an extension of NSManagedObjectContext to perform the fetch requests against the datastore.1

The following is what the Objective-C version of the -reloadData method looks like in the data handler.

// DataHandler.m
- (void)reloadData {
    self.runningClocks_private = nil;
    self.pausedClocks_private = nil;
}

- (NSIndexPath*)indexPathForAddClockCell {

    return [NSIndexPath indexPathForItem:self.runningClocks.count inSection:0];
}

- (NSArray*)runningClocks {

    return [NSArray arrayWithArray:self.runningClocks_private];
}

- (NSArray*)pausedClocks {

    return [NSArray arrayWithArray:self.pausedClocks_private];
}

- (NSMutableArray*)runningClocks_private {

    if ( _runningClocks_private == nil ) {
        _runningClocks_private = [[self.managedObjectContext clocksForParent:self.parentClock running:YES] mutableCopy];

    }
    return _runningClocks_private;
}

- (NSMutableArray*)pausedClocks_private {

    if ( _pausedClocks_private == nil ) {
        _pausedClocks_private = [[self.managedObjectContext clocksForParent:self.parentClock running:NO] mutableCopy];

    }
    return _pausedClocks_private;
}

What’s going on there?

We have a private variable used strictly within the data handler. When someone from the outside asks the data handler to reload the data, it just sets those private variables to nil. When someone from the outside asks for the public array of running or paused clocks, we copy the private array. If the private array is nil, we call the fetch method on the managed object context and store the result in the data handler; which could also be nil.

That’s a lot of nothing, or something representing nothing, going on.

The following code has the same results. However, we are still interfacing with an Objective-C version of the NSManagedObjectContext and a lot of nothing going on.

final func reloadData() {
    if let rClocks = managedObjectContext?.clocksForParent(parentClock, running: true) as? [Clock] {
        runningClocks = rClocks

    } else {
        runningClocks = [Clock]()

   }

    if let pClocks = managedObjectContext?.clocksForParent(parentClock, running: false) as? [Clock] {
        pausedClocks = pClocks

    } else {
        pausedClocks = [Clock]()

    }
}

That’s still a lot to read; let’s filter this to the part that really matters:

if let rClocks = managedObjectContext?.clocksForParent(parentClock, running: true) as? [Clock] {…}

What’s happening here?

We don’t need a separate property to store the data array, because, with Swift, we can mark a property as private(set), which means we want it to be stored, but read only.

  1. We don’t want the code inside the bracket to execute if we don’t have any clocks; this is a nil check.
  2. We may not have a managed object context; this is a nil check (?)
  3. The return value of a fetch request, as interpreted by swift, is an optional array of AnyObject; so, kind of a nil check.
  4. Because the return array is of AnyObject, we are asking Swift to only execute the inner code if the resulting array can be converted to an array of Clock objects ([Clock]).
  5. Going into the brackets, we don’t want our arrays to ever be nil; so, if the first condition fails (all prior steps), we instantiate an empty [Clock].

For one line of code, there is a lot going on there, which is why I try to avoid speaking in terms of how many lines of code does it take to get to the center of a Tootsie Pop—you can do a lot in a single line.

I decided to go ahead and convert the NSManagedObjectContext extension to Swift. This change included the non-optional return of [Clock]. Separating root (no parent) and child clock (has parent) methods. So, we are always guaranteed to have a return value of [Clock] and use different methods for whether we are looking for root or child clocks.

The result:

final func reloadData() {
    if let moc = managedObjectContext {
        if let pClock = parentClock {
            runningClocks = moc.clocksFor(pClock, running: true)
            pausedClocks = moc.clocksFor(pClock, running: false)

        } else {
            runningClocks = moc.rootClocks(true)
            pausedClocks = moc.rootClocks(false)

        }
    }
}

We’ve marked it final because we don’t want any class inheriting from this class to override this implementation. We still need to verify an NSManagedObjectContext exists. Then we need to check if a parent clock exists. Then we act accordingly.

The impressive part is not well formed code. The impressive part is the magic the compiler did to inform the decisions leading to this point.

Compiler Magic

Let’s go back to the original Swift implementation.

final func reloadData() {
    if let rClocks = managedObjectContext?.clocksForParent(parentClock, running: true) as? [Clock] {
        runningClocks = rClocks

    } else {
        runningClocks = [Clock]()

   }

    if let pClocks = managedObjectContext?.clocksForParent(parentClock, running: false) as? [Clock] {
        pausedClocks = pClocks

    } else {
        pausedClocks = [Clock]()

    }
}

In the NSManagedObjectContext extension, we changed the return type to a non-optional [Clock]. The compiler immediately let me know this bit of code would no longer compile. Therefore, I didn’t need to remember (or go hunting for) all the instances of the legacy call pattern.

Why won’t it compile?

Because, we will always return [Clock], even if it’s empty; therefore, the explicit cast from an array of AnyObject is not necessary. Further, the return type is non-optional, which is the only reason to have let something = some-optional-thing? in the first place. Therefore, we can rewrite it.

final func reloadData() {
    if let moc = managedObjectContext {
                 runningClocks = moc.clocksForParent(parentClock!, running: true)

    }

    if let moc = managedObjectContext {
        pausedClocks = moc.clocksForParent(parentClock!, running: false)

    }
}

Immediately we can see we are doing the same thing regarding the managed object context. Further, the compiler is complaining parentClock is optional and needs to be unwrapped (!) before being passed, because our method doesn’t accept optionals. So, in the interest of DRY and nil checking we do this:2

final func reloadData() {
    if let moc = managedObjectContext {
        if let pClock = parentClock {
                    runningClocks = moc.clocksForParent(pClock, running: true)
            pausedClocks = moc.clocksForParent(pClock, running: false)

        }
    }

}

But we still have the use case of looking at the root clocks of Time Journal ; so, we do the following.

final func reloadData() {
    if let moc = managedObjectContext {
        if let pClock = parentClock {
                    runningClocks = moc.clocksForParent(pClock, running: true)
            pausedClocks = moc.clocksForParent(pClock, running: false)

        } else {
                    runningClocks = moc.rootClocks(true)
            pausedClocks = moc.rootClocks(false)

        }
    }

}

The method name clocksForParent is redundant; so, let’s modify it:3

final func reloadData() {
    if let moc = managedObjectContext {
        if let pClock = parentClock {
                    runningClocks = moc.clocksFor(pClock, running: true)
            pausedClocks = moc.clocksFor(pClock, running: false)

        } else {
                    runningClocks = moc.rootClocks(true)
            pausedClocks = moc.rootClocks(false)

        }
    }
}

There we have it. Thanks to the compiler acting as an automated code reviewer, we have a much more readable bit of code.

Conclusion

I equate Swift combined with the compiler and Xcode to writing in the English language. English is the language; inherited from Roman and Latin roots with phonetic adoption of words from various other lanuages. The compiler and Xcode are like using writing style guides and editors to review your work. However, just like writing in any language and using any text editor, we should still be verifying we are making logical word and structure choices for our purpose. Luckily, the Swift language provides us with a visible, reserved, and searchable means of cleaning up our overuse of nil; namely the exclamation (!) and question (?) marks.4 Outside of extenuating circumstances, these symbols will only appear in your code if you are unwrapping an optional without checking or returning, storing, or passing an optional around.

In either case, I highly recommend reducing the use of optionals; code shouldn’t be like a box of chocolates.


  1. This is separation of duties again. The data handler does not run execute the fetch request itself; instead, it asks the NSManagedObjectContext to run a Time Journal specific query and return the results. return to text

  2. Unwrapping a nil pointer results in a fatal crash at runtime, and the compiler will trust that you know you can unwrap something. return to text

  3. If speak the full method name `final func clocksForParent(parentClock: Clock, running: Bool) -> [Clock] we get “A final definition of function named ‘clocks for parent’ parent clock running…”. So, “A final definition of function name ‘clock for’ parent clock running. return to text

  4. Find a text editor that allows you to see all the use of adjectives and pronouns; trust me. return to text

###