Function Naming in Swift

In this article, we will face a question that we should always ask ourselves when writing a function:

How should I name this function?

Although this question looks simple, getting it answered properly determines a crucial aspect in our professional lives as software developers. It makes our codebases cleaner and easier to use, as we will see.

The Importance of Clear APIs

If you were to use a function from a third party library, let's say for creating fancy labels, and you find these two options:

// A
static func makeLabel(withTitle title: String) -> FancyLabel
// B
static func configure(_ text: String) -> FancyLabel

Which one would you feel more comfortable with? A or B?

In case you are still hesitating, I'll give you a clue: common sense makes me think of option A, too.


Why option A, then…?

First, let's analyze why B isn't so good: Option B does not tell us what we are configuring exactly. Is it an existent instance? Does it create a new one? Does it return a new thing? It doesn’t tell us what's the meaning of the String it expects either. All we can tell beforehand is that it receives a String and returns a FancyLabel, but we don’t know exactly what it does. There is ambiguity and lack of information, and that's something we should avoid.

Option A, on the other hand, is totally explicit about these three things:

  • What it needs in order to work — a title
  • What it does* — it makes a label
  • What's the outcome — the created label
* We care about what it does (or is supposed to do), not about how it does that thing. We don’t care about the inner workings of the function. That’s how encapsulation works.

The usage of this function becomes very natural by just having these three things clear, because in this way we can know what the function goes about, without misunderstandings. As simple as it.

We will see it clearer when we take a look at how these functions are called:

let labelA = FancyLabel.makeLabel(withTitle: "Hello world") // clear
let labelB = FancyLabel.configure("Hello world") // not so clear

Now, what if we translated that example to a real life project with multiple developers working on it? What would you prefer to name your functions like? A or B? What would you prefer your teammates to name their functions like? A or B?

You can think of it like this: Each time you define a function, you are defining an interface that someone else will use in the future. Even if it’s private, and even if you think that nobody else will ever use it, it's always important that it is clear. Because any developer at any moment could need to deal with the internal code of any class in the project, even yourself. You never know!

In conclusion, if you put effort on naming your functions such that there is no ambiguity about what their inputs and outputs are, and what they do, a lot of effort can be saved when others have to call your function. They won’t have to go through its implementation to figure it all out. In this way, we save time and avoid misapprehensions that can lead to bugs. Defining clear interfaces in our code comes with the huge benefit of a codebase that is easier to deal with, to use, to shape, and to maintain.

Those extra seconds — or minutes — you need to think of the name of a function is time well invested.

Function Signatures in Swift

One of the most radical changes in Swift, if we compare it to Objective-C, and particularly since Swift 3.0, is the way function signatures work. Since that version, Swift is not only less verbose than its predecessor, but it's also clearer.

Let's take a look at an example:

dateLabel.text = [formatter stringWithDate: [[Date alloc] init]]];
dateLabel.text = formatter.string(with: Date())

We see two main differences here. First, the creation of the date instance is way more verbose in the first case, although that's only related to Objective-C's verbosity. Then, we can observe that the Objective-C version, besides lots of extra brackets, includes an extra word (Date) that the Swift one doesn't have.

This subtle — yet effective — change is actually possible thanks to the static nature of types in Swift. In the Swift version of the function, you are not able to pass in another object that is not a Date, because the compiler won't let you do that.

This change is simple, but it opened doors to a new world of function signatures. The code in general has become much more pleasant to read, as it's more concise and more similar to spoken language.

As mentioned, our next step is aiming to prune functions signatures, in such a way they become short enough to be concise, but still clear enough to avoid ambiguities.

Let's the prune begin…!

Pruning

Finally, let's analyze some examples of different functions signatures and see how we can make them look better by pruning, i.e. by removing some words.

// Signature
func moveView(view: UIView, toPoint point: CGPoint) { ... }
// Usage
moveView(view: headerView, toPoint: .zero) // ⚠️ long and redundant

It can become:

// Signature
func move(_ view: UIView, to point: CGPoint) { ... }
// Usage
move(headerView, to: CGPoint.zero) // 👏 clear and concise

Thanks to static typing, we don't need to specify that we are moving a view object in the function signature. Since the function requests for a UIView object, the only thing you can pass in is a that kind of object. Same with the point.

There are exceptions, though. You still need to specify what the parameter is about because its type might not be enough to describe it.

// Signature
func makeButton(withTitle title: String) -> UIButton { ... }
// Usage
let button = makeButton(withTitle: "Function Naming") // 👍 good

Let's see what happens when we try to prune here…

// Signature
func makeButton(with title: String) -> UIButton { ... }
// Usage
let button = makeButton(with: "Function Naming") // 👎 not clear

This happens because the type (String) does not exactly match the semantics of the content (a title). A String can represent lots of other things besides titles.

When in such a situation, there are two possible approaches that we can take. We either specify the semantics of the content by making the parameter name more explicit, as in the first part of the example above, or, we create a new type that describes the semantics more specifically, and use that new type as a parameter. In the example, we could have created a Title type for managing titles. The ideal scenario is going with the latter, as you will take even more advantages from the static typing system and your code will end up being more secure. I recommend you learn about techniques like phantom types and tagged types for this purpose.

Final Thoughts

  • Naming is hard. However, the time you spend on doing so is well invested. It reduces the possibilities of misunderstandings and prevents other developers — or yourself — from having to look at how all the code works trying to understand what it does. So, invest time in naming.
  • Take advantage of static typing. Make your APIs clear and concise.
  • Share this knowledge with your teammates and encourage good practices.
  • Practise! Practise! Practise!

And remember, a function should always be clear about 3 things:

  • What it needs — input
  • What it does — describe the process without exposing the inner workings
  • What it returns output

See you next time!

I hope this guide is useful for you, and I look forward to learning about your experiences and opinions in the comments below. Please, comment, suggest, and if you find it useful, share :)

Thank you for reading.


Follow us on social media platforms:
Facebook: facebook.com/AppCodamobile/
Twitter: twitter.com/AppCodaMobile
Instagram: instagram.com/AppCodadotcom

If you enjoyed this article, please click the 👏 button and share to help others find it! Feel free to leave a comment below.