Erica Sadun at Playgrounds Conference, 2017
Hello. My name is Erica Sadun, and today I'm going to talk about writing consistent Swift code.
For those of you who don't know me, I'm a writer, a blogger, a coder, and I write in particular about the Apple ecosystem and, specifically, about developer topics. My latest book is called Swift Style. It is from Pragmatic Press.
I thought it would be fun to explore why people bother following fussy and opinionated style guidelines. Style rules aren't enforced by the compiler, and they aren't mandated by any spec. They're not official even in the slightest because style rules are personal or organizational opinions on specific styling choices that make your code "Swiftier."
Why should you style your code? This is an important question because styling code involves a lot of work and overhead. This work goes beyond program correctness. Other than patting yourself on the back and congratulating yourself on how awesome your code looks, why should you invest time and effort developing and mandating consistent style in your projects? It's because conventional style is like a window. Just like you look through a window at the world outside, you look through code to see its intent, and code should essentially be invisible.
When there's a streak on the window or an unconventional use in your code, a programmer is going to fixate on the wrong thing. It's not just the compiler that needs to read your code. Well-written code, like a window, should not draw attention to itself. Code readers don't stop and say, "Wow, that's a especially well-written code." They only pay attention when you have failed somehow in your styling choices.
It's natural to pick up on weird style choices. This is the same impulse, the same human fundamental impulse that distracts you when you notice that humongous water spinach between somebody's teeth.
Conventional styling lets code fade into the background. Good style lets you focus on the underlying meaning and the direction of your code statements rather than the programmatic details that went into crafting that code.
Good style lets you tuck away details. Looking at code at a more abstract level, this reduces the mental effort needed to understand the code, because when you use inconsistent or unexpected styles, the work you have to do to understand code grows larger and more prominent.
Good style lets you chunk information together, so you're thinking about code in terms of its goals, not about a specific keyword or symbols that are used to express it.
All team members should follow your house guidelines, and this holds true even when individuals prefer or are used to a different style. Adopting a house style means that code is going to look the same regardless of the person who produced it. When you perform code reviews, those reviews are going to stay focused on implementation and not on inconsistent uses of spaces or commas or other trivia like that.
Consistent style lowers maintenance costs, makes code reading easier. It reduces the time it takes any team member to get up to speed and, in a nutshell, consistent rules produce consistent code.
I obviously cannot dive into every point of Swift style. You'll all have homes. You have lives to return to at some point over the next week, so I'm going to present just a few quick topics to give you a taste of the kind of choices you make when building a personal or house style.
Let me start with semicolons. Lots of Swift coders arrive from C-like languages. C-like languages use semicolons as statement separators.
In Swift, you can also use semicolons, but you don't want to use them at the end of every line. When you're ending lines with semicolons, it's not "Swifty." It's not necessary. Yes, your code will compile, but you're out of step with the styling conventions of the greater Swift community.
Terminal semicolons aren't needed in Swift, and they're a pretty reliable indicator of someone who's either new to the language or who very firmly does not want to adapt their coding style to the prevailing convention.
As a note of caution, both of these can realistically act as warning signs in some hiring situations, so you want to be very careful in incorporating semicolons into code that you share during the hiring process. What you want to do instead in Swift is to use semicolons to join related statements together, and let me give you an example of what I mean by this because this is a very different way of using and looking at semicolons compared to, say, Objective C.
Does anybody here remember postfix increment? It was the operator that returned the current value and then incremented it afterwards, and it's no longer in the Swift language. You can create postfix increment by joining a defer statement with return. The code in the snippet that you're seeing now uses the defer statement, and it executes when the current scope ends.
By combining these together on a single line with a semicolon, it joins the two thoughts together and creates one unified statement about how an expectation of a compound product should act. This is a really good place for semicolons because those two statements are fundamentally linked. As the late departed Douglas Adams might have said, "This is a kind of holistic programming, and it takes advantage of the fundamental interconnectedness of these statements."
Semicolons allow you to pair setup with cleanup, and that's a really great use for them. You might establish a new drawing context on the UIKit stack, and the defer statement that you add right next to it remembers to pop that context once the scope completes. You have setup and cleanup all joined together. It's a terrific use of semicolons.
Style example number two, number literals. When you format your number literals, you enhance their readability. What you do is you insert underscores, and this does wonders for making your numbers more inspectable.
Compare these two declarations. When you insert underscores every three digits, you improve the way the numbers in your code can be read. It can be understood, and it can be checked. Take a look at these examples to get a sense of how underscores improve basic legibility for this large number.
You should always choose the right spacing. For example, when writing hex numbers, you want to go two by two, not three by three.
Here's an example.
It's hard to tell whether the first of these has the correct number of Fs. Now, imagine you're inspecting this code. Are there six Fs there? Are there five? It takes time to count them out. It's something that's not really a necessary task if you insert underscores because, when you do that, your values are easy to read, to check and to confirm at a glance without too much work. Good style always reduces the work involved in inspecting code.
Style example number three, typing. When I talk about typing here, I'm not talking about sitting at a keyboard and punching keys. I'm talking about whether you should declare a type for a symbol or let the compiler infer the type from an instance constructor. There are various situations where you want to weigh the two of them against each other, for example, when declaring collections. For the most part, you want to prefer typing, and let me show you some examples.
Here are a bunch of collection declarations. The top items use explicit typing, and the bottom items do not. At the top, each declaration states the symbol's type, and then they use collection literals to initialize the value. It's a very clean approach. The bottom examples, in contrast, use a whole bunch of different constructions where the types are set by compiler inference, so why should you prefer the top examples over the bottom ones? The obvious answer is; what happens when you want to incorporate initial values.
As you see in this updated example, the construction approach is far less adaptable. You have to add entire new lines to change the initial values, so you should always prefer a style that lends itself to easy modification. When you put the types on the left of the equal sign, it increases inspectability, code readability, and it lets you use these literals as you need to for easy-to-update, easy-to-modify initialization.
Meaningful constructors use arguments that help establish a new instance. Although type declarations help with collections, it's not always the right solution. For example, you can't construct a point with a literal. You have to use an initializer. Prefer constructors that use values when you create an instance.
In this example, it's fine to skip explicit typing. Compare the first and the second example. There's plenty of information just to the right of the equal sign to establish the type. Adding a type is consistent, but is also wordy and redundant. I'm not saying this is a bad style to adopt, because I love consistency. It's just unnecessary in many cases to include the type explicitly.
Style is always about readability and maintainability, and, in this case, the simpler version easily hits both these points. When several versions hit the points, always prefer the simpler one.
My fourth example, and this is going to be the final example before I try to get into the key thought points, and that's Optional Sugar.
Like any expressive programming language, Swift gives you a lot of ways to do things to get your results. In the case of optionals, prefer the question mark version, also called the sugared approach, over any explicit .some case, and let me once again give you some examples.
Both of these switch statements perform the same work, but the first example uses explicit optional cases, which is very oriented on design detail; and the second uses a postfix question mark, which is some .some syntactic sugar. It basically hides the implementation details. As you see, the sugared version is simpler. It is easier to read, and these are important goals when setting style guidelines.
Next, consider iterating through a list of optional items. Which of these two is better? Is it the sugar one at the top? Is it the explicit one at the bottom? Which one would you go for? This is actually a trick question because, in this case, the simpler method is to use flatMap.
Sometimes, you get so caught up in measuring the quality of individual style choices, you miss out on the bigger picture. Style isn't just about specific syntax choices. It's also about adopting best overall practices. In this case, that best practice is to use flatMap.
Case let, guard let, they're always extremely awkward choices. Unless you're performing actual enumeration conditional case binding, as in the example you see now, try to find another approach. Both if case and guard case should be used cautiously and sparingly.
I want to review all the things I showed you in those examples and summarize some really critical lessons about style. Ready?
Well-written code doesn't distract you. It doesn't grab attention away from its underlying intent and its goals. Good style lets you focus on meaning when reading your code, not on braces, not on stray semicolons, not on spacing. Weird styling choices are spinach caught in your code's teeth. Focus on meaning.
A strong style guide enables you to adopt consistent approaches throughout your code and throughout your organization. Focus on your core guiding principles, readability, simplicity, maintainability. Well-considered rules produce consistent code. That consistency is going to naturally extend throughout large projects. Good style will lower your maintenance cost. It will reduce the time it takes new team members to get up to speed, so be consistent.
Members of your house team should always defer to the house approach even when individuals prefer or are used to different rules. It's essential to establish a uniform style throughout a project if you're going to benefit from having those style rules in the first place, so be uniform.
No matter how valuable your existing code base is to you, avoid copy-paste coding. Prefer Swift style and Swift idioms when programming in Swift. No matter how well you auto-convert, you're doing your code a disservice. Copy-paste code is difficult to maintain and it misses many of the safety features built into the safe language. If you're seeing lots of semicolons at the ends of lines, you probably haven't fully bought into Swift as its own level. Develop Swift in Swift.
When you're writing code, think about how the code is going to be read and maintained in the future. Simple practices like adding explicit typing to the left of the equal sign when you're doing collections, supports the person who's going to maintain your code in the future, even if that person is you. When you simplify inspection, you make it more robust and you lower the cost associated with getting back up to speed with that code when it comes time to evolve and maintain it.
Take advantage of styling cues like number formatting to make your code more readable. The simple act of inserting spacing between number groups is just one way to reduce the burden on anyone reading your code, so, when writing code, think about readability.
Swift includes a number of features that simplify common operations. Prefer to use these syntactic sugar features whenever they're available. They've been added to the language quite deliberately to make coding, at their point of use, better. There's no reason you should use more complicated solutions when these simpler sugared solutions are there for you, so, where possible, prefer sugar.
Don't get so focused on tweaking tiny implementation details that you can't walk away and think about the larger picture. Picking the right approach is just as important as choosing the right spacing. When you're building your house style rules, incorporate best practice rules as well as ones about things like commas and spaces and semicolons. Avoid tunnel vision and try to see the larger picture when building your house guidelines.
Let me conclude this talk with a quick too-long, didn't-read key point, and that's this: “It's not just the compiler that needs to read your code”.
If you'd like to check out some more opinionated explorations about writing Swift code, my book is available in the Pragmatic Beta Program right now, and the URL is right there on the screen. The book will be hitting print soon and it will be available from, hopefully, all the major, usual retailers.
I want to thank everybody for having me here, and I'll be happy to answer your questions now. Thank you.
If you enjoyed this talk, you can find more info: