Adding nil coalescing to result

It’s not rare to have code that uses Swift’s Result in a simplified way. Not all call sites need to handle the error and may choose to throw the error away, opting instead to provide a default value. There are a many ways to achieve this goal - here’s three alternate spellings to achieve the same thing:

let deliveryStatus: String

switch lookupDeliveryStatus() {
case let .success(value):
    deliveryStatus = value
case .failure:
    deliveryStatus = "In progress..."
}

Or

let deliveryStatus: String

if case let .success(value) = lookupDeliveryStatus() {
    deliveryStatus = value
} else {
    deliveryStatus = "In progress..."
}

Or

let deliveryStatus = (try? lookupDeliveryStatus().get()) ?? "In progress..."

The last example is pretty short but it’s quite noisy with a try? that is wrapped in () and then a usage of the nil-coalescing operator (??). We can reduce a lot of the noise by defining an implementation of the nil-coalescing operator on the Result type directly.

extension Result {
    public static func ?? (result: Result, defaultValue: @autoclosure () throws -> Success) rethrows -> Success {
        switch result {
        case .success(let value):
            return value
        case .failure:
            return try defaultValue()
        }
    }
}

With this in place our original example becomes much more succinct

let deliveryStatus = lookupDeliveryStatus() ?? "In progress..."

The above felt a little uncomfortable to begin with because the thing returning a Result obviously felt it was important enough to provide some additional context in the event of a failure so surely we should pay attention to it. I think this tension is interesting as it causes a pause to consider whether you really should be handling the error or are happy to ignore it.

Something that eases the tension for me is that the actual implementation is a copy/paste/tweak job from the function defined to work on optionals, so there is prior art that it’s reasonable to provide a convenience for this operation.

git rebase --exec

In this post we’ll look at the --exec option on git rebase. We’ll run through a couple of concrete examples and explain why you might consider them useful.


In a nutshell the --exec flag allows you to run a command against a range of commits starting at the point you specify and finishing on the latest commit. Some worked examples will hopefully make this a little clearer.


Unit testing all commits

Having a clean history that compiles on each commit is really helpful when debugging especially for times when you have to reach for git bisect.

To validate that all commits are clean before pushing to the remote it would be useful to run the unit tests on every commit. That might go a little like this:

Given the following history

* cadc5160 Integrate onboarding screen into main app
* 47e01676 Implement onboarding screen in sample app
* f5ff3165 Add onboarding-screen feature flag
*   e156c2b3 Merge pull request #1 from feature/update-terms-of-use
|\ 

We can grab the sha of the commit before the new work (in this case e156c2b3) and then run

git rebase e156c2b3 --exec "swift test"

The result will be that git will check out each commit in turn and run swift test. If the tests fail on any of the commits then git stops the rebase and waits for us to resolve the issues.

If you perform the above before pushing you can guarantee that all the commits are in good order. You can also use the same mechanism in your CI pipelines if you want to enforce that each commit should compile and pass tests.


Running a code formatter/linter

Teams often run some kind of code formatter or linter to catch common issues and avoid the same conversations occurring over and over in code reviews. Running these tools is great but ideally we’d want to ensure they are run for every commit to avoid code churn. To illustrate the issue with code churn look at this watered down example:

Below we have 3 commits on a project to the same area in one file. The first commit is implementing a bug fix, the second is adding new work and the last commit is a result of running a formatting tool.

git churn

The result of this history is that when there is an issue and we run git annotate on the file to find out the motivation for the changes we end with all of our changes being attributed to the very unhelpful “Run formatter” commit message. If we wrote meaningful messages on the original commits then that effort is now more difficult to find and causes us to reach for more involved git commands like git log --follow.

This is another great use case for git rebase --exec as we can run the formatter/linter on each commit before we push to the remote. If there are any commits that fail the formatter/linter then git will pause the rebase, wait for us to fix the issues and then continue once we have resolved them.


More

The --exec argument can run any command and decides to continue the rebase or stop depending on the exit status of the command. This means you can write any custom command or program that you’d want to run against a range of commits to verify things and as long as you use an appropriate exit status then git will automatically continue or stop.

lldb language tip

I often find myself wanting lldb to evaluate an Objective-C expression when the current language is Swift. I’ve drastically improved my debugging experience by adding a couple of command aliases to make this a quick thing to do.

In my ~/.lldbinit I’ve added the following:

command alias set-lang-objc settings set target.language objective-c
command alias set-lang-swift settings set target.language Swift

Why?

Here’s a couple concrete examples of how changing language can make things simpler

Calling private selectors from Swift

Calling non public selectors is a pain in Swift. A few selectors I commonly want to use are:

+[UIViewController _printHierarchy]
-[UIView recursiveDescription]
-[UIView _parentDescription]
-[NSObject _ivarDescription]

In order to call these in Swift I have to use value(forKey:) - this is more characters to type and includes speech marks, which my fingers are generally slightly slower and more error prone at locating.

po UIViewController.value(forKey: "_printHierarchy")
po someView.value(forKey: "recursiveDescription")

Swapping the language to Objective-C removes a lot of the boilerplate typing:

set-lang-objc
po [UIViewController _printHierarchy]
po [someView recursiveDescription]

Calling methods in Swift when you only have a pointer address

Often I don’t have a variable in my current frame as I’m just grabbing memory addresses from other debug print commands or the view debugger (awesome use case).

Once you have a memory address in Swift you have to do the following to call a method:

po unsafeBitCast(0x7fd37e520f30, to: UIView.self).value(forKey: "recursiveDescription")

Changing the language to Objective-C this becomes considerably simpler:

set-lang-objc
po [0x7fd37e520f30 recursiveDescription]

One more thing…

In addition to the two aliases above I’ve had the following alias (that came from StackOverflow at some point) in my tool bag for a long time. It’s really helpful for single shot commands where I want to evaluate Objective-C without changing the language for subsequent commands.

Back in my ~/.lldbinit I have:

command alias eco expression -l objective-c -o --

Which allows me to run a single command in Objective-C whilst the language is currently set to Swift

eco [0x7fd37e520f30 recursiveDescription]