Functions

Declarations

Functions in Swift are nothing special. We start them with the keyword func followed by the function name and then the arguments enclosed in parenthesis: func (args).

Parameters should be formatted (name: type). When arguments are assigned to parameters, they become constants. We can get around this within the function by assigning the parameter to another variable–just be conscious of this if you're getting mutating-constant errors.

Finally, if there is a return type, you should put -> <return type> after the parameters. If there is no return type, you either leave out the arrow or write void for the return type–this will be more important when we talk about closures, otherwise, you can basically forget about Void.

func <name>(parameter1: type1, ... parameterN: typeN) -> <return type> { body }

Alright let's start with some basic functions!

First, a function that returns the string "Hello World!":

func helloWorld() -> String {
    return "Hello World"
}
// helloWorld: () -> String

Fun little side note, you have a one-line function, you can omit the return statement because the compiler will just assume that the expression is being returned.

func helloWorld() -> String {
    "Hello World"
}
// helloWorld: () -> String

Okay, let's spice things up by adding some arguments. We'll add a function that prints out someone's name (given their name).

func greeting(name: String) {
    print("Hello \(name), have a nice day :)")
    // We can use \() to insert information into string
    // It's equivalent to doing "Hello " + name + ", have a nice day :)"
    // This works for all data types.
}
// greeting: (name: String) -> Void

Calling Functions

Calling a function is pretty easy. All you have to do is <function name>(parameter: value...)

Let's call our previous functions

helloWorld()
// "Hello World": String
greeting(name: "Noah")
// "Hello Noah, have a nice day :)"
// Thank you I will :)

Anonymous Arguments

You may have noticed that Swift is different than some other languages because you need to include the parameter name. We can change this, per parameter, by making them anonymous. You do this by adding a wildcard (_) in front of the parameter name in the function header:

func iDontNeedLabels(_ no: String, _ labels: String, _ here: String) -> String {
    no + " " + labels + " " + here
}
//iDontNeedLabels : (_: String, _: String, _:String) -> String
iDontNeedLabels("time", "to", "nap")
// "time to nap" : String

Functions as Values

Swift functions are higher order meaning that they can be passed and stored as values! You may have noticed the type annotations in the previous code samples... And to store a function in a variable you're gonna need to understand their types... time to get comfortable with them! Side note, the notation I use above is actually slightly different because it acknowledges parameter names.

These types are not hard. For the arguments we write a tuple of the argument types. This is then followed by an arrow -> and then the return type (you need void here if there is no return type). For example, helloWorld, which is helloWorld() -> String can be stored in a variable like

let aGreeting: () -> String = helloWorld

Similarly, greeting, which was greeting(name: String) -> Void can be stored like:

let anotherGreeting: (String) -> Void = greeting

Now that we have these functions in variables, we can call them by enclosing their arguments in parenthesis, separated by commas and ignoring parameter names:

aGreeting()
// "Hello World" : String
anotherGreeting("Aaron")
// "Hello Aaron, have a nice day :)"

Exception Handling

If a function has a possibility of raising an exception directly (not an assertion error, fatal error, or runtime error) we must state this in the function, so that callers can handle the error. To do this, we write throws before the arrow pointing to the return type: func someFunction() throws -> Void.

If a function takes a function as an argument that has the potential of throwing an error, this new function must deal with it. If we are just going to throw the error again, we use the rethrows tag instead.

func someFunction(anotherFunctionThatThrows: () throws -> Void) rethrows -> Void

This is very rare, so you don't really need to worry about it.

Closures

We can also create functions directly as anonymous functions. You will need to store it somewhere, and if it is going into a variable, you will need an explicit type. If it is directly into a function as an argument, your type will already be specified.

Arguments are given names by default–they are named $ + whatever their index is in the arguments, so the first is $0, the second is $1 and so on. But these are not pretty, so sometimes you may choose to rename them. To do this, at the beginning of your closure write your names separated by commas followed by the keyword in. It's like your saying "these variables are in the rest of the code".

// returns if [$0] < [$1]
let lessThan: (Int, Int) -> Bool {
    $0 < $1
}

// returns if [$0] < [$1]
let namedLessThan(Int, Int) -> Bool { left, right in
    left < right
}

Escaping

This is an advanced concept (you also don't need to understand it until the end of the course).

If a function (the main function) takes a function (the inner function) as an argument, and if the inner function is not done resolving by the time the main function returns, then we have a problem. The inner function is being stored in the main function, so if we erase the main function (because it is done being executed), then we cannot execute the inner function because it will be gone! To fix this, we change the type declaration of the inner function to be tagged @escaping. This will handle this problem for us. I think the compiler will tell you when you mess this up, but I won't guarantee that, so if you're playing with higher-order functions keep it in mind.

func mainFunction(innerFunction: @escaping () -> Void) {}

Partial Application

Partial application is a method of creating new functions by applying some arguments to a previous function. Partial application in Swift is somewhat manufactured–the experience is not as wonderful, magical, amazing... etc. as in OCaml. In many functional langauges, you can just apply a few arguments and it will automatically apply it for you. In Swift, we have to be more intentional. To curry, in Swift, you have to write a new function that applies it for you. Let me show you what I mean. Let's say we have some function curry of type(Int, Double, String) -> String. Let's say we want to do partial application with an Int of 3 and a String of "Martha". We create a new function that takes in the remaining arguments, applies them, but also applies 3 and "Martha".

let curriedFunction: (String) -> String = { text in
    return curry(3, text, "Martha")
}

Last updated