To get anything done in Swift, you'll need to learn the ins and outs of functions. Functions are exceptionally powerful and flexible in Swift. The basics are simple—especially if you've worked with other programming languages before—but because of Swift's flexible syntax it can easily become complex if you're not familiar with the basics.
In this article, we'll focus on those basics first and explore the more complex syntax and use cases in the next article. It's important that you don't skim the basics as they are essential to understand where a function's power comes from in Swift. Let's start with an example to dissect the anatomy of a function in Swift.
1. Learn by Example
A function is nothing more than a block of code that can be executed whenever it's needed. I'd like to start with an example to discuss the basic anatomy of a Swift function. Fire up Xcode and create a new playground. Add the following function definition to the playground.
func printHelloWorld() { println("Hello World!") }
A function begins with the func
keyword and is followed by the name of the function, printHelloWorld
in our example. As in many other languages, the name of the function is followed by a pair of parentheses that contain the function's parameters, the function's input.
The body of the function is wrapped in a pair of curly braces. The printHelloWorld
function contains one statement in which we print the string Hello World!
in the standard output. This is what a basic function looks like in Swift. The syntax is simple, clean, and minimalist.
You can invoke the function by typing the name of the function, followed by a pair of parentheses.
printHelloWorld()
2. Parameters
Let's make the above example a bit more complex by adding parameters to the function definition. This simply means that we provide the function with input values it can use in the function's body. In the following example, we define the printMessage
function, which accepts one parameter, message
, of type String
.
func printMessage(message: String) { println(message) }
A function can accept multiple parameters or input values. The parameters are wrapped by the parentheses that follow the function's name. The name of the parameter is followed by a colon and the parameter's type. As you remember, this is very similar to declaring a variable or constant. It simply says that the message
parameter is of typeString
.
Instead of printing a hard-coded string like we did in the printHelloWorld
function, we print the message
parameter's value. This makes the function flexible and more useful.
Invoking the function is very similar to what we saw earlier. The only difference is that we pass in an argument when invoking the function.
printMessage("Hello World!")
Note that the terms parameters and arguments are often used interchangeably, but there is a subtle, semantic difference in Swift. In Swift, parameters are the values specified in the function definition while arguments are the values passed to the function when it is invoked.
Multiple Parameters
As I mentioned earlier, the syntax of functions is very flexible and it shouldn't surprise you that it's perfectly possible to pass multiple arguments to a function. In the next example, we create a variation on the printMessage
function that allows us to print the message multiple times.
func printMessage(message: String, times: Int) { for i in 0..<times { println("\(i) message") } }
While the name of the function is identical to that of the original printMessage
function, the function's type is different. It's important you understand the previous sentence. Read it again.
Each function has a type, consisting of the parameter types and the return type. We'll explore return types in a moment. Functions can have the same name as long as their type is different as shown by the previous two function definitions.
The type of the first function is (String) -> ()
while the type of the second function is(String, Int) -> ()
. The name of both functions is the same. Don't worry about the ->
symbol. Its meaning will become clear in a few moments when we discuss return types.
The second printMessage
function defines two parameters, message
of type String
and times
of type Int
. This definition illustrates one of the features Swift has adopted from Objective-C, readable function/method names. While the name of the function is printMessage
, by reading the names of the function's parameters, it's easy to understand what the function is supposed to do.
In the second printMessage
function, we create a for-in
loop to print the message
string times
times. We use the half-open range operator, ..<
, as we saw earlier in this series.
When we start typing printMessage
in the playground, Xcode displays both functions in the autocompletion menu. Thanks to the function's type, it's easy to choose the function we're interested in. Calling the second printMessage
function is as simple as:
printMessage("Hello World", 3)
Default Values
One of my favorite features is the ability to define default values for parameters. This may sound silly if you're coming from a language that has had this feature for ages, but this is pretty great if you've been working with C and Objective-C for many years.
In short, Swift allows developers to define default values for the parameters of a function. Let's define a new function that prints the current date in a specific format. Make sure you add the following import statement at the top of your playground to import the UIKit framework.
import UIKit
Let's first define the printDate
function without making use of default values for any of the parameters.
func printDate(date: NSDate, format: String) { let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = format println(dateFormatter.stringFromDate(date)) }
If you're not familiar with the Foundation framework and you're not understanding what's happening in the function body, then that's fine. The focus of this example isn't on the implementation of formatting the date. In printDate
, we use the value of the format
parameter to format the value of date
. If we don't pass in a value for the format
parameter, Swift will let us know by throwing an error.
We can remedy this by defining a default value for the function's second parameter as shown in the updated function definition below.
func printDate(date: NSDate, format: String = "YY/MM/dd") { let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = format println(dateFormatter.stringFromDate(date)) }
Defining a default value is as simple as specifying a value in the list of parameters in the function's definition. The result is that Swift will no longer complain and the error disappears.
printDate(NSDate())
What if we do want to pass in a value for the format
parameter? Let's try it out and see what Swift tells us.
Because the format
parameter has a default value and is therefore optional, we need to pass in the argument's name to tell Swift what parameter we're referring to. The fix is very simple as you can see below.
printDate(NSDate(), format: "dd/MM/YY")
Note that Apple recommends to position parameters with a default value at the end of the list of parameters. This is certainly a good idea and common in most other programming languages that support optional parameters.
3. Return Type
The functions we've seen so far don't return anything to us when we invoke them. Let's make the printDate
function more useful by returning the formatted date as a string, instead of printing the formatted date from within the function. This requires two changes as you can see below.
func printDate(date: NSDate, format: String = "YY/MM/dd") -> String { let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = format return dateFormatter.stringFromDate(date) }
The first thing we change is the function's definition. After the list of parameters we specify the return type, String
. The return type is preceded by the ->
symbol. If you've worked with CoffeeScript, then this will look familiar.
Instead of printing the formatted date using the println
function, we use the return
keyword to return the value from the function. That's all we need to do. Let's try it out.
let formattedDate = printDate(NSDate(), format: "dd/MM/YY") println(formattedDate)
We invoke the printDate
function, store the returned value in the constant formattedDate
, and print the value of formattedDate
in the standard output. Note that the name of the printDate
function no longer reflects what it does so you may want to change it to formatDate
.
No Return Type
The other functions we've defined in this tutorial didn't have a return type. When a function doesn't have a return type, it isn't necessary to include the ->
symbol in the function definition.
A few paragraphs earlier, I told you that none of the functions we had defined returned a value to us. That's actually not entirely true. Let me explain the nitty-gritty details with an experiment. Add the following line to your playground and see what happens.
This is interesting. Swift doesn't have a problem that we store the return value of the printHelloWorld
function in a constant, but it does complain that it is unable to infer the type of the value
constant.
What's happening here? Every function in Swift returns a value, even if we don't define a return type in the function definition. When a function doesn't explicitly specify a return type, the function implicitly returns Void
, which is equivalent to an empty tuple, or ()
for short. You can see this in the playground's output pane as shown in the above screenshot.
We can get rid of the above warning by explicitly declaring the type of value
, an empty tuple. I agree that it's not very useful to store an empty tuple in a constant, but it illustrates you that every function has a return value.
let value: () = printHelloWorld()
Tuples
Another great feature of Swift is the ability to return multiple values from a function by returning a tuple. The following example illustrates how this works. Let me repeat that it's not important that you understand how the timeComponentsForDate
function does its job. The focus is the return value of the timeComponentsForDate
function, a tuple with three elements.
func timeComponentsForDate(date: NSDate) -> (hour: Int, minute: Int, second: Int) { let dateComponents = NSCalendar.currentCalendar().components((.CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitSecond), fromDate: date) let hour = dateComponents.hour let minute = dateComponents.minute let second = dateComponents.second return (hour, minute, second) }
The function accepts one argument, an NSDate
instance, and returns a tuple with three labeled values. Labeling the tuple's values is only for convenience, it is possible to omit the labels.
func timeComponentsForDate(date: NSDate) -> (Int, Int, Int) { let dateComponents = NSCalendar.currentCalendar().components((.CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitSecond), fromDate: date) let hour = dateComponents.hour let minute = dateComponents.minute let second = dateComponents.second return (hour, minute, second) }
However, as the following example illustrates, labeling the values of the tuple returned from the function is very convenient and makes your code easier to understand.
let timeComponents = timeComponentsForDate(NSDate()) println(timeComponents.hour) println(timeComponents.minute) println(timeComponents.second)
It's also possible to return an optional value from a function if there are scenarios in which the function has no value to return. This is as simple as defining the return type of the function as optional as shown below.
func timeComponentsForDate(date: NSDate) -> (hour: Int, minute: Int, second: Int)? { let dateComponents = NSCalendar.currentCalendar().components((.CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitSecond), fromDate: date) let hour = dateComponents.hour let minute = dateComponents.minute let second = dateComponents.second return (hour, minute, second) }
Conclusion
In this tutorial, we've focused on the basics of functions in Swift. It's important that you understand the syntax of functions, because in the next article we will explore more advanced functions that build upon what we've covered in this tutorial.
I encourage you to read the article again if necessary and, more importantly, write a few functions in a playground to become familiar with the syntax. The basics are easy to understand, but you only get the hang of them by practicing.