Introduction to Functions in Swift

      2 Comments on Introduction to Functions in Swift

It might seem strange at first to write an article on functions in swift. But functions in swift are a bit special and have some pretty cool characteristics. Hopefully by the end of this article you'll understand them better.It might seem strange at first to write an article on functions in swift. But functions in swift are a bit special and have some pretty cool characteristics. Hopefully by the end of this article you’ll understand them better.

Three Things to Remember

There are only three things that you should remember from this post. Functions can:

  1. be assigned to variables
  2. capture variables outside of their local scope
  3. be defined with a ‘func’ keyword or with a closure expression ‘{}’

Assigning Functions to Variables

This is a cool one. Functions can be assigned to variables. Your variable type will be the signature of the function. And you can pass your functions as parameters to other functions, basically, treat it like any other variable. Let’s see a quick example of this:

func assignVehicle(vehicleID: Int) {
    print("Assigned vehicle \(vehicleID)")
}

func assignDriver(driverID: Int) {
    print("Assigned driver \(driverID)")
}

var assign: (Int) -> () = assignVehicle

assign(23) // prints: Assigned vehicle 23

assign = assignDriver

assign(42) // prints: Assigned driver 42

func handleAssignment(assignmentHandle: (Int) -> ()) {
    print("Will do something meaningful here :)")
    assignmentHandle(12)
}

handleAssignment(assignmentHandle: assign)
// prints:
// Will do something meaningful here :)
// Assigned driver 12

We have a var ‘assign’ with a signature of ‘(Int) -> ()’ and our functions ‘assignVehicle’ and ‘assignDriver’ have the same signature. If we assign our ‘assignVehicle’ function to the ‘assign’ variable we can call the function using the assign variable ‘assign(23)’. Same goes for ‘assignDriver’ function. You can see in the ‘handleAssignment’ functions how we’re actually passing the function as a parameter into another function.

At this point you might be getting confused, and you might be thinking, what’s the point of all of this. Once you get used to it, it actually makes your code more readable. Take your array sort for example. Instead of passing a closure expression to your sort, you can have your sort functions defined and assigned to variables, so you can reuse them, or sort the array based on different parameters.

Beware of recursion:

func assignDriver(driverID: Int) {
    print("Assigned driver \(driverID)")
    assign(driverID)
}

If you’re not careful and assigned your ‘assignDriver’ to your ‘assign’ variable you’ll end up with a recursive call here (thanks ingconti :)).

Capturing Variables Outside of Their Local Scope

This goes without saying, but functions capture variables outside of their local scope. Think of it this way. You can access ‘self’ from within a function body (and you can access the instance variables, of course). This will become more important with escaping closures. For now, just keep this in mind.

‘func’ and Closure

Apart from declaring your functions using the ‘func’ keyword, you can also declare them using the closure expression. Let’s modify our example from before:

func assignVehicle(vehicleID: Int) {
    print("Assigned vehicle \(vehicleID)")
}

var assign: (Int) -> () = { vehicleID in
    print("Assigned vehicle \(vehicleID)")
}

assign(23) // prints: Assigned vehicle 23

func assignDriver(driverID: Int) {
    print("Assigned driver \(driverID)")
}

assign = assignDriver

assign(42) // prints: Assigned driver 42

Instead of assigning the ‘assignVehicle’ function to the ‘assign’ variable we defined the function using the closure expression. These are anonymous functions, so the only way to use them is to actually assign them to a variable.

Using It

Once you understand these three important properties of functions in swift, your daily development life will be so much easier. You’ll be a lot more comfortable using/reading swift code. Swift is using closure expressions a lot through the language, and so will you in your code. One example where you’ll use closures is in your networking layer. You’ll probably want to call a closure expression when your networking call is finished, it might look something like this:

    func restCall(urlString: String, onCompleted: ((Data?) -> ())?) {
        
        guard
            let url = try? urlString.asURL()
            else {
                onCompleted?(nil)
                return
        }
        
        // perform networking call
        onCompleted?(responseData)
        }
    }

@escaping Closures

Closures can be passed as arguments to functions. They can be split into two types. Non-escaping (default for swift 4) and escaping. Non-escaping closure will be executed before the function finishes. If you mark your closure as @escaping this means that your closure will be executed after the function finishes. It’s said that the closure is ‘escaping’ the function. This is one very important difference between the two.

You will see escaping closures a lot in asynchronous programming. If you call a function, and within it you call some other function that does some heavy processing, pass in your closure and dispatch it on a separate thread. Your main function will exit long before your closure gets called.

Here it’s important to remember that functions capture variables outside of their local scope. Meaning, your escaping closure will capture local variables outside of its scope. If you’re not careful, this might lead to retain cycles and memory leaks. This is why we use capture lists with escaping closures. An example of a capture list would be:

dataProvider.getListItems { [weak self] (items) in
   self?.tableView.refreshControl?.endRefreshing()
   self?.items = items
   self?.tableView.reloadData()
}

Here we used a capture list where we ‘weakified’ self in order to prevent the closure from keeping a strong reference to self. You will see this pattern being used through your day-to-day development.

@autoclosure

This is a nice feature of swift where the compiler will wrap the passing argument in a closure. It’s used for short circuiting ‘&&’ and ‘||’ operators, for assertions, preconditions and fatal errors. But the most common usage of it is for the nil-coalescing operator. Let’s inspect it in swift source:

public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
    rethrows -> T {
  switch optional {
  case .some(let value):
    return value
  case .none:
    return try defaultValue()
  }
}

Hopefully, you’re familiar with this operator. Just to refresh your memory. This operator will try to unwrap an optional, if the optional is nil, it will use a default value. For example:

var someInt = Int("22") ?? 2
print(someInt) // prints: 22

If you didn’t use the autoclosure your code would look something like this (note: this won’t compile, it’s just an example):

var someInt = Int("22") ?? { 2 }
print(someInt) // prints: 22

So, autoclosure is just a bit of syntactic sugar that keeps the code nice and easy to read. Diving a bit deeper into short circuiting and nil-coalescing operator we see that the default value (in case of nil-coalescing) is evaluated only when needed. This is a great optimisation technique if your default value is an expensive operation.

Conclusion

We covered some basics on functions in swift. And hopefully this will give you a kick-start and steer you in a right direction if you want to explore the topic further. I hope your understanding of functions in swift is a bit better now and that you learned something new today 🙂 And, like I mentioned at the beginning of the article, if you take anything away from this article, take those three points from the top.

As always, have a nice day 🙂

Dejan.

More resources

2 thoughts on “Introduction to Functions in Swift

  1. ingconti

    nice article.
    but I am a quick pedantic…

    at:
    Infinite LoopSwift
    func assignDriver(driverID: Int) {
    print(“Assigned driver \(driverID)”)
    assign(driverID)
    }

    it’ is NOT an infinite loop, is a recursion, program will not run forever, it will consume all stack and die..

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.