Defer

Created June 9, 2023

What is it?

defer is a keyword in Swift.

A defer statement looks like this:

defer { ... }

Inside of the defer statement , within the brackets, you have executable code.

defer { print("Executable code") } 

This executable code can be one or more code statements. In the example below the print() is a code statement and we have two of them:

defer { 
	print("Code statement 1")
	print("Code statement 2")
}

You can have one or more defer statements in a scope. In this example, the scope is the scope of someFunction():

func someFunction() {
	defer { print("Defer statement 1") }
	defer { print("Defer statement 2") }
}

What does it do?

A defer statement isolates a piece of code you want to run right before you leave the scope.

For example, I have a function that will print out a story:

func tellAStory() {
	print("Once upon a time...")
	print("There was a girl named Robin")
	print("who wrote a blog about defer statements.")
	print("Everybody loved her blog, and learned a lot.")
	print("Robin was very happy.")
}

// Command:
tellAStory()

// Output:
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
Robin was very happy.

I want all of my stories to end with the phrase “the end,” so I add a defer statement that prints “the end”:

func tellAStory() {
	defer { print("The end.") }
	print("Once upon a time...")
	print("There was a girl named Robin")
	print("who wrote a blog about defer statements.")
	print("Everybody loved her blog, and learned a lot.")
	print("Robin was very happy.")
}

// Command:
tellAStory()

// Output:
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
Robin was very happy.
The end.

Notice how I added the defer statement at the top.

Notice in the output how “the end” appears as the last line of the story.

This is the magic of the defer statement: it will always be run right before we leave the current scope, no matter what line I place it on.

It “defers” or “postpones” the execution of some code until the very end.

This means that these three versions of tellAStory() will all have exactly the same output:

func tellAStory() {
	defer { print("The end.") }
	print("Once upon a time...")
	print("There was a girl named Robin")
	print("who wrote a blog about defer statements.")
	print("Everybody loved her blog, and learned a lot.")
	print("Robin was very happy.")
}

func tellAStory() {
	print("Once upon a time...")
	print("There was a girl named Robin")
	defer { print("The end.") }
	print("who wrote a blog about defer statements.")
	print("Everybody loved her blog, and learned a lot.")
	print("Robin was very happy.")
}

func tellAStory() {
	print("Once upon a time...")
	print("There was a girl named Robin")
	print("who wrote a blog about defer statements.")
	print("Everybody loved her blog, and learned a lot.")
	print("Robin was very happy.")
	defer { print("The end.") }
}

// Command:
tellAStory()

// Output:
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
Robin was very happy.
The end.

Anything else I should know?

Yes! Things get weird when you have multiple defer statements within the same scope.

Let’s add a second defer statement to our story function:

func tellAStory() {
	defer { print("The end.") }
	defer { print("Everyone lived happily ever after.") }
	print("Once upon a time...")
	print("There was a girl named Robin")
	print("who wrote a blog about defer statements.")
	print("Everybody loved her blog, and learned a lot.")
	print("Robin was very happy.")
}

You expect the output to be:

// Expected Output
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
The end.
Everyone lived happily ever after.

But actually, the output looks like this:

// Actual Output
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
Everyone lived happily ever after.
The end.

When you have multiple defer statements within the same scope, you start at the bottom and move toward the top.

func someFunction() {
	defer { print("Third") }
	defer { print("Second") }
	defer { print("First") }
}

// Command:
someFunction()

// Output:
First
Second
Third

When should I use it?

defer statements are rare, but you may see them in code that works with files.

Because it’s important to close a file after you modify it, you can use a defer statement in your scope that will make sure you never forget to close the file.

func someFunction() {
	defer { closeFile() }

	// open file
	// modify the file
}

Best Practices

It’s a good practice to avoid using defer statements unless you have a very good reason (like the file example above).

Because defer statements have an unnatural execution order, they can make your code harder to read and understand. We expect our code to be executed from top to bottom within a scope, and the defer behavior goes against that expectation.

Conclusion

The defer statement is an interesting piece of Swift that can “defer” or “postpone” a bit of code so it is run right before you leave the current scope. While useful when working with files, it should be used with caution to avoid making your code hard to understand.