To get anything done in Swift, you 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, functions can become confusing if you're not familiar with the basics.
In this article, we focus on the basics first. Then we'll go on to 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. Let's start by dissecting the anatomy of a function in Swift with an example.
1. Learn by Example
A function is nothing more than a block of code that can be executed whenever it's needed. Let's look at an example of 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() { print("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 input to the function.
The body of the function is wrapped in a pair of curly braces. The printHelloWorld()
function contains a single statement that prints the string Hello World!
to 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(message:)
function, which accepts one parameter, message
, of type String
.
func printMessage(message: String) { print(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 as 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(message: "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(message:times:)
function that allows us to print the message multiple times.
func printMessage(message: String, times: Int) { for i in 0..<times { print("\(i) \(message)") } }
While the name of the function is identical to that of the original printMessage(message:)
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(message:times:)
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 and method names. While the name of the function is printMessage
, it's easy to understand what the function is supposed to do by reading the names of the function's parameters.
In the second printMessage(message:times:)
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(message:times:)
function is as simple as:
printMessage(message: "Hello World", times: 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(date:format:)
function without making use of default values for any of the parameters.
func printDate(date: Date, format: String) { let dateFormatter = DateFormatter() dateFormatter.dateFormat = format print(dateFormatter.string(from: date)) }
If you're not familiar with the Foundation framework and you don't understand what's happening in the function body, then that's fine. The focus of this example isn't on the implementation of formatting a date. In printDate(date:format:)
, 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, the compiler throws 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: Date, format: String = "YY/MM/dd") { let dateFormatter = DateFormatter() dateFormatter.dateFormat = format print(dateFormatter.string(from: 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 the compiler no longer complains and the error disappears.
printDate(date: Date())
Even though we have specified a default value for the format
parameter, we can still pass in a value if we want to.
printDate(date: Date(), format: "dd/MM/YY")
Note that Apple recommends positioning 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(date:format:)
function more useful by returning the formatted date as a string, instead of printing the formatted date in the function's body. This requires two changes, as you can see below.
func printDate(date: Date, format: String = "YY/MM/dd") -> String { let dateFormatter = DateFormatter() dateFormatter.dateFormat = format return dateFormatter.string(from: 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 print(_:separator:terminator:)
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(date: Date(), format: "dd/MM/YY") print(formattedDate)
We invoke the printDate(date:format:)
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(date:format:)
function no longer reflects what it does, so you may want to change it to formatDate
instead.
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 warn us that the type of the returned value is not what we might think it is.
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 and it is also mentioned in the warning the compiler outputs.
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 to 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(date:)
function does its job. The focus is the return value of the function, a tuple with three elements.
func timeComponentsForDate(_ date: Date) -> (hour: Int, minute: Int, second: Int) { let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date) let hour = dateComponents.hour let minute = dateComponents.minute let second = dateComponents.second return (hour ?? 0, minute ?? 0, second ?? 0) }
The function accepts one argument, a Date
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: Date) -> (Int, Int, Int) { let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date) let hour = dateComponents.hour let minute = dateComponents.minute let second = dateComponents.second return (hour ?? 0, minute ?? 0, second ?? 0) }
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(Date()) print(timeComponents.hour) print(timeComponents.minute) print(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: Date) -> (hour: Int, minute: Int, second: Int)? { let dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date) guard let hour = dateComponents.hour else { return nil } guard let minute = dateComponents.minute else { return nil } guard let second = dateComponents.second else { return nil } return (hour, minute, second) }
Conclusion
In this tutorial, we explored the basics of functions in Swift. It's important that you understand the syntax of functions, because in the next article we'll explore more advanced functions that build on what we 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.
If you want to learn how to use Swift 3 to code real-world apps, check out our course Create iOS Apps With Swift 3. Whether you're new to iOS app development or are looking to make the switch from Objective-C, this course will get you started with Swift for app development.
- SwiftCreate iOS Apps With Swift 3
- SwiftCode a Side-Scrolling Game With Swift 3 and SpriteKit
- iOSWhat's New in iOS 10