Skip to content

Functions

Informations presented on this page may be incomplete or outdated. You can find the most up-to-date version reading my book Learn Swift by examples. Beginner level
Prepared and tested with Xcode 8.3 and Swift 3.1

In this tutorial we cover the following topics


Functions


Basic informations

In short, Swift functions can be characterized as follow:

  • Each function parameter has both an argument label and parameter name.
  • We write an argument label before the parameter name, separated by a space.
  • The argument label is used when calling the function.
  • The parameter name is used in the implementation of the function.
  • By default, parameters use their parameter name as their argument label.
  • All parameters must have use unique names.
  • It is possible for multiple parameters to have the same argument label.
  • If we don't want to use an argument label for a parameter, an underscore character must be used as label for that parameter.
  • If we use an underscore character, we cannot use a parameter name as an argument label.
  • If a parameter has an argument label, the argument must be labeled when we call the function.
  • Although arguments have their labels, we cannot change arguments order.
  • In-out parameters cannot have default values.
  • Variadic parametes (parameters which accepts zero or more values) cannot be marked as in-out.

Let's start our tests...

Results are


functionSimplestForm
functionSimplestFormVersion2
noParameters
Parameter value: foo
Parameter value: foo : bar

Let's continue...


No return value
maxValue: Optional(9), maxPos: Optional(4)


first:second:third:fourth:fifth
definedByUser defaultValue
definedByUser alsoDefinedByUser


3.0
before swap: 3 5
after swap: 5 3
Program ended with exit code: 0


Functions as type

Every function in Swift has a specific function type consisting of the parameter types and the return type of the function.

For example, function defined as

is of the type () -> Void while function

is of the type (String, String) -> String.

A function type is used just like any other type.


8

In consequence we can use function type as a parameter type for another function.


8

where add is a previously defined function.

An interesting question is: How we can use a function type as the return type of another function? An answer: As any other existing type.


8


Nested functions

Swift allowed us to do more with functions: it can be seen as strange and awkward but we can define functions inside the bodies of other functions -- this way we have so called nested functions to distinguish from previously discussed global functions. Nested functions are hidden from the outside, but of course can be called by and used by enclosing function. An enclosing function can also return one of its nested fnctions which allow the nested function to be used in another scope.


-1


Closures

Although closures are very close related with functions we will describe them in separate section because of the complexity and some new ideas. Closures are self-contained blocks of code that can be passed around and used in our code. Closures in Swift are similar to blocks in Objective-C (see Blocks section in Objective-C, Classes, part 2 tutorial) and to lambdas in other programming languages.

Closures are a technique for implementing lexically scoped name binding. We can think of it as a record storing a function together with an environment. Each variable that is used locally, but defined in an enclosing scope is associate with the value or storage location to which the name was bound when the closure was created. What is important, a closure, unlike a plain function, allows the function to access those captured variables through the closure's reference to them, even when the function is invoked outside their scope.

In short, closures are Swift’s anonymous functions.

General idea

To understand how it works, let's analyze the following example (this code is in JavaScript, so you can test it in browser's developer tools)

In line

an anonymous function is defined. Variable x is defined outside of this function but is used inside. So, we can say, that this functon needs x to work corectly. And there is nothing surprising that call

and in consequence

works and returns correct values


inside foo, call to f(): 2
call to f(): 3


The queestion is how it is possible that code

also works?! Now we are outside of foo function and we try to call f(). How it could be that f() knows value of the x variable? This is when closures comes into play.
The assignment statement

''saves'' function but also all the outer environment needed to execute this function (in this case, x variable).

Now consider a second example (again in JavaScript).

This code should print 10 and 14 as a result.

The above code defines a function referenceValue with a parameter x and a nested anonymous function. The nested function has access to x, because is in the lexical scope of x (note that x is not local to this anonymous function). The function referenceValue returns a closure containing

  • the inner anonymous function, which adds the y value to the x (reference) value,
  • and a reference to the variable x from this invocation of referenceValue, so inner function will know where to find it when invoked.

Note that, as referenceValue returns a function. This means that both moveFrom3By and moveFrom5By are of function type, so we can invoke moveFrom3By(7) and moveFrom5By(9). Interesting is that while moveFrom3By and moveFrom5By refer to the same anonymous function, the associated environments differ, and invoking the closures will bind the name x to two distinct variables with different values (3 and 5) in the two invocations, thus evaluating the function to different results (10 and 14).


Basic usage

General closure expression syntax has the following form:

This syntax is very closde to function syntax but without name (for this reason we say about anonymous functions). The parameters can be in-out, named variadic parameter and tuples. We cannot use default values for them. Using previously defined doSomethingWithTwoInts(::) function we have

Because it is always possible to infer the parameter and return type when passing a closure to a function or method as an inline closure expression, so we never need to write an inline closure in its full form when the closure is used as a function or method argument.

What is more, single expression closure can implicitly return the result by ommiting the return keyword from declaration.

Even this short expression can be shorthanded. In Swift a shorthand argument names $0, $1 and so on can be used to refer to the values of the closure's first, second and so on arguments. If the argument is a tuple $0.0 is a key, and $0.1 is a value of the closure's first argument.

If you think that there is no option we cen write shorter expression, you are wrong. We can use operator methods (see operator methods description -- to do --). From their definition Swift can infer the number and the types of arguments and return value.
Todo (id=no todo id): Prepare operator methods description

If the closure is a final argument of a function we are going to use, and the closure expression is long, we can write it as trailing closure. A trailing closure is a closure which is written after the function call's parentheses (but it is still an argument to the function).


If the closure expression is provided as the function or method's only argument and we provide that expression as a trailing closure, we don't need to write a parentheses after the function or method's names when we call it.

In the example below we will show very basic but useful example of closure usage. Please have in mind that in the code below we will create two constants, which in some sense will behaves like variables. This is because functions and closures are reference types. So event if we define a constant related with a closure this relation is constant (doesn't change) but the closure this constant refers to is still able to change some captured variables.


2
-2
4
-4
6
-6


Other topics related to closures: escaping closures and autoclosures will be described in the separated advanced material.
Todo (id=no todo id): Describe escaping closures and autoclosures in separated material