Cleaning Up With Swift Defer

The Swift defer statement isn’t something you’ll need to use often. It becomes useful when you want to be certain that you clean up resources before they go out of scope. I came across a good use case when working with security scoped iCloud files.

What Is Defer?

From The Swift Programming Language:

You use a defer statement to execute a set of statements just before code execution leaves the current block of code.

The block of code might be a method, an if statement, a do block or a loop. The body of the defer statement runs regardless of how you leave the block, including throwing an error. This makes it perfect for running clean up code to close files or release other system resources.

A Practical Example

Here’s a practical example when working with the document picker. In this case I present the picker for the user to choose an image file:

let picker = UIDocumentPickerViewController(documentTypes:
             ["public.image"], in: .open)
picker.delegate = self
present(picker, animated: true)

You get a callback to the UIDocumentPickerDelegate with the URLs for the files the user chose:

func documentPicker(_ controller: UIDocumentPickerViewController,
     didPickDocumentsAt urls: [URL]) {
  for url in urls {
    choseFile(url)
  }
}

The files might not be local to my App. They could be external, stored in iCloud or another provider which requires I security scope the access. The system only allows our App to open a limited number of security scoped files so we need to balance the start and stop access calls.

After watching the relevant WWDC session the code to access the file looks like this:

func choseFile(_ url: URL) {
  let didStartAccessing = url.startAccessingSecurityScopedResource()
  defer {
    if didStartAccessing {
      url.stopAccessingSecurityScopedResource()
    }
  }

  doSomething(url)
}

The scope of the defer statement in this case is the method. So our cleanup code, stopping the access, is the last thing to happen before we return to the caller.

It’s not necessarily intuitive until you get the hang of it. I’m tempted to write the following:

if didStartAccessing {
  // WARNING: 'defer' statement before end of
  // scope always executes immediately
  defer {
    url.stopAccessingSecurityScopedResource()
  }
}

Xcode will warn you that this is pointless as the defer would always execute at once since its block scope is now the if statement. The original example keeps the defer statement at the method level making it the last thing that is executed after we return from doSomething. We can then test if we were accessing a security scoped file and stop the access.

The Small Print

There are some caveats to be aware of when using defer:

No Escape

You cannot escape from the defer statement. So you cannot break or return or do something that might throw an error:

defer {
  guard didStartAccessing else {
    // ERROR 'return' cannot transfer control
    // out of a defer statement
    return
  }
  url.stopAccessingSecurityScopedResource()
}

Reverse Order

I didn’t need it but you can have more than one defer statement. They are executed in the reverse order that they are written:

defer {
  print("1")
}

defer {
  print("2")
}

// 2 - last defer executes first
// 1