Skip to main content

Creating a Save Panel Completion Handler with a Closure in Swift

·3 mins

Swift is hot now. I figured I should share some of what I learned converting some Objective-C code to Swift.

The Example #

The example in this article is a method to export data to a file. The example opens a save panel and calls the save panel’s completion handler. The completion handler checks if the user clicked the Save button. If the Save button was clicked, get the URL that represents the file location. Use that URL to export the data to the file. This example should serve as a guide to using Swift closures in your code.

The Code #

Here’s the code.

@IBAction func export(sender: AnyObject) {
	let savePanel = NSSavePanel()       
	savePanel.beginWithCompletionHandler { (result: Int) -> Void in           
		if result == NSFileHandlingPanelOKButton {               
			let exportedFileURL = savePanel.URL               
			// Save the data. What you do depends on your app.
			// Don't just paste this code in your app as your app
			// probably doesn't have a createPDF() method.                                			self.createPDF(exportedFileURL)
		}
	} // End block   
}

Let’s look at the first line.

@IBAction func export(sender: AnyObject)

The @IBAction specifies that this method is an Interface Builder action. I made the method an Interface Builder action so I could call the method from the application’s user interface and make connections in Interface Builder. Interface Builder actions take a sender as an argument. If you don’t need your method to be an Interface Builder action, you can remove the @IBAction and replace the sender argument.

Now let’s look at the second line.

let savePanel = NSSavePanel()

This line opens a generic save panel. You may want to customize your save panel to replace the Save button with an Export button or add a menu that lets you choose an export file format. But I’m keeping things simple here.

The Completion Handler #

The third line is the most complicated line in the method.

savePanel.beginWithCompletionHandler { (result: Int) -> Void in

It calls NSSavePanel’s beginWithCompletionHandler method, which waits for the user to finish interacting with the save panel, either by clicking the Save button or the Cancel button. When the Save or Cancel button gets clicked, the completion handler gets called. The completion handler is a closure, which is the Swift equivalent of an Objective-C block.

Declaring a closure differs from declaring a function on where you place the arguments and return value. In a function you place the arguments and return value before the {. In a closure you place the arguments and return value after the {.

In a Swift closure, the arguments come before ->, and the return value after ->. The closure in this example takes one argument: an integer named result. The closure doesn’t return a value. Use Void as the return value when a closure doesn’t return a value. Use the in keyword to finish declaring the closure.

After declaring the closure, writing the code is relatively simple. Write your code and add a closing } to complete the closure. My example is pretty simple.

if result == NSFileHandlingPanelOKButton {               
	let exportedFileURL = savePanel.URL
}

All the code does is check if the user clicked the Save (OK) button. If so, get the file location URL from the save panel. The file location URL represents where the user wants to save the data. Use the URL to write the exported data to a file. How you write the data depends on your application and is beyond the scope of this article.