Mohamed Omar

100 Days of SwiftUI - Day 7

It's finally time to learn about functions in Swift.

Day 7 of 100 Days of SwiftUI covers the basics of functions, returning values and defining parameters.

In Swift, we use the func keyword along with a name and parentheses. We also execute the function by using the function's name with parantheses.

This would define the function:

func sayHello() {
  print("hello!")
}

And this would execute it:

sayHello()

Parameters (and arguments)

Functions allow us to reuse code all over our app, but that reusability wouldn't be very exciting if we couldn't customize it in some cases.

For example, sayHello() is great if I only ever wanted to print "hello". But what if I wanted to print something else? That's where parameters come in.

A parameter is an option that we can pass into function definitions to customize them. Suppose we had a function called printMessage(). We could tell it to look for a message parameter that can be used in the function body.

func printMessage(message: String) {
  print(string)
}

message: String is telling our function to expect a string called message — an argument — when the function is used. A parameter carves a slot in your function. An argument is the value that is placed into that slot.

Returning values

Functions become even more useful when they return customized values based on the arguments you passed in.

To return values, we can use the aptly-named return keyword. This keyword, however, has some quirky (to me, at least) gotchas in Swift compared to JavaScript.

For starters, a function that uses the return keyword has to indicate the type of the value being returned.

This would error out:

func lowercaseMessage(message: String) {
  return message.lowercased()
}

//ERROR: Unexpected non-void return value in void function

While this works:

func lowercaseMessage(message: String) -> {
  return message.lowercased()
}

print(lowercaseMessage(message: "HELLO")) // "hello"

This is a neat built-in method to ensure type safety, which I will somehow mess up at least 50% of the time.

The return keyword can also be skipped altogether if the function's body can be reduced to one line.

So the above function can be shortened to:

func lowercaseMessage(message: String) -> {
  message.lowercased()
}

print(lowercaseMessage(message: "HELLO")) // "hello"

Returning multiple values

To return multiple values in a function, we need to reach for different data types. Arrays and dictionaries work, but they have some drawbacks.

Arrays, by their nature, will require us to figure out the specific index for each item we want to access. Dictionaries will allow us to use keys, but we will also have to pass in a default value for each key we try to access, in case that key doesn't exist.

Tuples, on the other hand, give us a bit more freedom. By returning a tuple from a function, we can access an item using its key and let Swift worry about checking if they're available or not.

This function, for example, would return a tuple that we can dig into to access city and country.

func getLocation() -> (city: String, country: String){
  (city: "Example city", country: "Example state")
}


var location = getLocation()
var city = location.city
var state = location.state

print(city) // Example city
print(state) // Example state

This is great, but there's an even easier way to access properties from a tuple. Similar to JavaScript's destructuring for objects, we can pluck out properties from a tuple directly like so:

func getLocation() -> (city: String, country: String){
  (city: "Example city", country: "Example state")
}

let (city, state) = getLocation()

print(city) // Example city
print(state) // Example state

Parameter/argument labels

When passing an argument into a function, we (generally) have to indicate the name of the parameter its filling.

So if I call the lowercaseMessage from earlier and don't pass in the name of the parameter, I'd get an error:

func lowercaseMessage(message: String) {
  return message.lowercased()
}

lowercaseMesssage("HELLO") // Missing argument label 'message:' in call

To get around this, we can tell Swift to skip checking for the parameter name at the function's call site. We do that by passing in an underscore and a space right before the parameter name.

func lowercaseMessage(_ message: String) {
  return message.lowercased()
}

lowercaseMesssage("HELLO") // hello

We can also create a different name for the argument. Say we wanted to refer to a parameter as "message" inside the function. But at the call site we wanted that parameter to be called "phrase".

We can do that by passing in that external name right before the internal one.

func lowercaseMessage(phrase message: String) {
  return message.lowercased()
}

lowercaseMesssage(phrase: "HELLO") // hello

This could be useful in situations where we want the function name to "flow" with the parameter name. Reading code like sendMessageTo(user) looks a little nicer than sendMessageTo(id), for example.

That's it for the meaty lesson on functions in Swift! I'll get to Day 8 as soon as I can, but I first have to take a brief intermission and add pagination to my blog page. Stay tuned for a blog post on that soon!