Yes, but…

Fritz Anderson
Jul 23, 2017 · 5 min read

… and the article mentions this in passing (“not always the right option”)…

I’d like to emphasize the “not always,” separately from the focus of the article. This is a comment, not a refutation.

Let’s consider the case of

UIViewController.present(_:animated:)

and the alternative

.immediatelyPresent(_:); .animatePresentation(of:)

My instincts chirp a bit about the repeated parameter (passing different ones is an option?); and that under the proposed API “immediately” does not mean “immediately.” Also that animation will default to the opposite of what you’d expect. Leave those aside, they’re quibbles, it’s just an example.


Somewhere deep in UIKit there will be something that causes the new controller to be presented, by animation or not. We don’t need to see the source code to know that this must be a single routine. We know that it will not happen immediately — it has to wait for its turn on the main dispatch queue.

In other words, .present(_:animated:) is still there, the proposed API simply wraps it in two calls that will have to be coalesced. My instincts chirp at impedance mismatches like this.


Next problem. Suppose I have a hierarchy of view controller classes. My concrete controller subclass wants to present a modal view. The controller superclass has policies and state of its own that subclasses shouldn’t worry about.

  • The superclass may unconditionally pass “animated” up to UIKit. No branch on “animated.”
  • It may unconditionally force “animated.” No branch on “animated.”
  • It may override “animated” depending on policy or state. That’s an if/switch, but not on “animated.”
  • It may override based on the class or state of the new controller. Another if/switch, but not on “animated.”
  • My instincts tell me the superclass’s branching on “animated” is a corner case. The most common would be that initialization (such as preparing transition animations) will depend on whether the subclass wants to animate.

If you’re thinking about eliminating branches in the rare case of affirmatively caring about animation, how often are you simply farming the same complexity out to the caller? How often does the subclass have to break the superclass’s encapsulation to know what the superclass wants to do?

How often does refactoring the branch and the condition to _each and every_ subclass commit them to the stability of the superclass implementation? Or do we encapsulate all of the superclass’s criteria into a single func returning Bool (a stable API for what you pass as a parameter is a tough nut), thus returning most of the complexity to the superclass by way of an extra function call that every subclass has to remember to make?

I pause to repeat: the article is meant to call attention to an issue, not to take it to such absurd extremes.

Separating the calls implies — says outright — that they may be called separately. Completely separately, not necessarily in the same function or call stack. Not necessarily on the same child controller. Not necessarily in order. Not necessarily before the child controller goes on screen. Not necessarily before it is dismissed and deallocated. My instincts chirp that the API should not sponsor these.

The two-step (immediately/animate) forces the superclass to implement both, because it has to ensure that a subclass doesn’t set/reset animation after the superclass resets/sets it. (You’ll need dontAnimatePresentation(of:), but you still can’t keep the subclass from calling that behind your back, so that’s another requirement on the superclass.)

If the superclass does initialize depending on the subclass’s preference for animation, it must delay initialization until the subclass doesn’t call animatePresentation(of:). UIKit has the luxury of knowing that les jeux sont faits when the coalesced presentation event pops out of the main queue. The presenting controller won’t know the deadline has arrived.

Imagine a workaround: When the presentation event arrives at the runloop — we can wonder whether that’s too late for a smooth transition — UIKit notifies the presenting controller that the child will appear. The presenting controller can look up what it meant to do with that child and revise “animated” as needed… possibly by branching on “animated” exactly the way it could have if it had called .present(_:animated:) when it had better knowledge and control.

(The solution in this case is to have two methods, immediatelyPresent(_:) and immediatelyPresentAnimated(_:). The article and this comment use this API to illustrate a general concern that may not always be so easy to resolve. Humor me.)


In the case of that DataLoader completion: What if I care only that the operation completed, no matter how — is my code simpler if I have to write the identical handler twice? (The article says always. I’m prepared to doubt it.) That’s a trivial case of success and failure having common code paths such as the article shows — do you trust yourself to forever maintain their copies of that code? A wrapper function for the common code mitigates the risk (at the expense of adding smaller ones), but you’ll never eliminate it.

And… do multiple code paths become Good Things if the paths and the conditional that selects them don't fit on the same screen? The branch is probably selected in third-party code; two funcs with several lines of identical content may be in the same file, but probably not the same screen. Again, the common wrapper function mitigates the problem. of visibility, but not likely better than a single handler.

The example in the article would, I observe, be 50% longer if documented, and much shorter if the “cause” for each section were commented at the single place it is executed instead of being discoverable only through searches of the IDE’s symbol and call-chain indices.


“Duplication is better than inappropriate sharing.” Correct. “X is better than inappropriate Y” is always correct. If X is better than Y, that’s what makes Y inappropriate. Whether X is better is still a judgment call.


People seem to like Swift’s guard and defer constructs. But I imagine a branch-counting tool counting every guard, and every assignment and condition in the guard, as an additional branch; worse, an early exit; worse, an early exit into COME FROM code stacked up by defer clauses. Surely they'd rack up the complexity points and lint warnings like nobody’s business.

And yet we think of guard/defer as easier to understand than the alternatives. Perhaps there is more than one kind of complexity.


If “complexity” means anything, it means that the poor schmo who has to maintain the code (even his own) should be able to make out what it does. This is the 67% of coding that is human-to-human.

In a world of optimizers, “complexity” (in this sense) is 1% a technical problem, 99% cognitive. Even a branch-counter that could tally across libraries, modules, classes, or files (which is where most complexity has been for the last twenty years) would lack the empathy it needs for 99% of what makes complexity a problem. Single figures-of-merit are not a magic bullet.


What the article teaches is that programmers should have code complexity on their radar so they can apply their judgment; one way to think of it is in terms of branching logic. Good advice.

You should prosecute branches. You should resolve choices at the highest practical level. “God functions” that handle each and every contingency on a dozen paths are stenches, not smells. If you’re constantly branching on some “type” property of a class instance, it’s time to think about refactoring the types into separate classes. If a significant part of a function is mostly-isolated from the rest, give thanks and refactor. Complexity is a bug-in-waiting. (Hellooooo technical debt!)

In this, the article is absolutely right.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade