Beginning Swift Programming Part 5 — Functions, Enums, and Scope

Bob Roebling
Swift2Go
Published in
16 min readApr 3, 2018
Photo by Brook Anderson on Unsplash

In the last post, we covered if statements, while loops, and for-in loops (usually just called for loops).

Some of those if statements were pretty large, and what if we wanted to do multiple things in that if statement? They would become huge and hard to read. That’s what today is all about. Let’s dive right in.

Functions

You can actually write a program in Swift without any functions. I’m sure people coming from other languages are thinking, oh yes you do! The truth is you really don’t. We will talk about this more in the scope section but code written at the global scope is used as the entry point for your program. The entry point is just a way of saying, this is the code that runs first when your program starts. Easy enough, right?

We write functions because it breaks our code up to make it easier to read. We can follow a couple of programming principles that are really just guidelines to make your job easier, this is not an exhaustive list but it’s enough to get you thinking about how you can make your code better:

  • Separation of concerns — if you count the letters in a String then add up two numbers, it would make sense to take these two separate functions, one function to count the letters in Strings, and another to add numbers.
  • Single responsibility principle — this is summed up with the phrase: everything has one job, and each thing does its job well.
  • Don’t Repeat Yourself (DRY)— this is pretty obvious, Why type the same text more than once? Why type the same text more than once? It drives you crazy knowing that I did that if you are a type A personality, but type A developers go crazy when they see the same thing written over and over again, and here’s the bad news, almost every developer reading your code is using a type A mindset when they do.

Did you get that? Good! We will go over more later but for now, this is a good foundation.

Let’s start by showing you an example of a function written in Swift. The syntax may look strange but we will cover it all.

I know this may look confusing at first, but let's go through this.

First, we define a function using the func keyword. This lets the compiler know, hey I want this next block of code to be re-usable.

Next, we give our function a name addTwoNumbers.

Functions have parenthesis after the name, if there are no arguments, we leave it empty like this:
func doStuff() { /* doing stuff */ }
If there are arguments (things we can pass into the function) then we include them in the declaration. In our example above we do have arguments, two to be exact, we have number and otherNumber. These aren’t very descriptive in the example but they should be.

You might notice that I used number firstNumber and otherNumber secondNumber. number and otherNumber are what we will refer to these arguments in future calls so we know which argument we are setting the value for. firstNumber and secondNumber are what will be used in the function’s body. The body of a function is all of the code between the open and closing curly braces { /* this is the body */ }.

Next, we have this part number firstNumber: Int. Int is the type we will expect to use when we call this function. By using number firstNumber: Int we are saying the name of the first argument is number the variable we will create for use in the function is named firstNumber and it is of type Int. The same applies for otherNumber secondNumber: Int.

Finally, we have this weird syntax at the end, outside of the parenthesis.
-> Int

This is just simply when this function ends, we should return an integer value. Return is just a way of saying exit this function and return whatever value is to the right of return. In our example above we create a constant named functionResult which is the sum of firstNumber and secondNumber, then we return functionResult.

When we call the function we actually using var result = ... because we need to remember the value in a constant or variable that was returned from the function. If our function wasn’t supposed to return anything we’d get an error, if our function was supposed to return something and we didn’t capture the value, we’d get a weird warning result of <functionName> is unused, consider using _ =.

_ = what is this? Underscores are used when you just don’t care. Either you don’t care what the value is that is returned, or you don’t care to use an argument name for the first variable in your function.

That’s right, you can omit the first variable’s argument name. Let’s see how that looks:

See how nice that looks? Since we are just returning the sum without performing any additional logic, we can just use
return firstNumber + secondNumber
And since we talked about descriptive names lets fix this a little:

Did you see what happened there? firstNumber used an underscore so we just had a blank placeholder where we could insert our first number into the parameters. secondNumber didn’t have an argument name, so it defaulted to the variable name. And most importantly, it still makes sense to us. Just to cover all bases here’s a quiz. What if I did this:

func add(firstNumber firstNumber: Int, to secondNumber: Int) -> Int {...}

How would I call that function? Would it read like English? or French? or whatever other programming language you decided to write your code in (as long as it was still syntactically correct)?

If I gave you read/write access to one file in my program and you tried to call this function would you need to guess at what it does?

I wouldn’t say this is a perfect example, but code clarity, especially in functions is what you should strive for so be thinking about this constantly.

So we wrote a function several different ways, each way is correct. You have complete control over how it reads and what argument names and variables will be used with it.

The print statement print("Hello, World!") is actually a function that returns a String value. We use it to display variables in a console (think of this as a read-only terminal window or command prompt). The really cool thing is it knows how to cast, or convert, one type to a String, such as an Integer to a String or even a dictionary. I won’t say it will always look pretty, because sometimes it will not, but it is used extensively when building your program and during debugging. Debugging a program is just another way of saying something went wrong and now we are trying to figure out where the problem is. We will talk more about debugging later on with a fun story, but for now, let’s just stick with the print statement for figuring out what our issues are.

From here on out, I’m going to use print("something") rather than
// result is equal to "something" because it is how you’d expect to use it in a real program.

Functions do not necessarily have to return a value, instead, we could nest the print statement to handle error logging for us like this:

Now we have taken print("Aww...") and encapsulated it into a function that we can call any time we need to. This is called decomposition. If composition is building how a section of code works, decomposition is extracting the code re-used over and over again and making it easier to reference. For this simple example, we could have just written the print statement since it was only one line, but for the sake of brevity, perhaps a bit of laziness, this should be sufficient to get the idea.

By this function not returning a value, when it finishes we don’t have to use the return keyword. We can use the return, but by the time all of the code inside the body has been performed and we’ve reached the closing curly brace the function returns anyways. The only time you might use the return keyword in a function that doesn’t return a value is when you want to exit the function before all of the code has run.

NOTE: If you put in a return statement outside of a conditional check, the compiler will whine saying “this code will never be executed.” It’s just a tip from Xcode saying “we know you did this but here is an opportunity for you to clean up the code that will never be used, or fix your return statement so the code below has a chance to run.”

Enumerations (Enums)

Photo by Carlos Alberto Gómez Iñiguez on Unsplash

I chose this picture of a stoplight because it represents what an enum is. Enums provide options to your program. Each enum defines a set of associated items where you can pick only one at a time. Let’s see how we define an enum.

Now in programming, and you may have noticed, we use camelCase to name variables. Camel Case is super simple to learn, just take a phrase, remove the spaces, lowercase the first letter of the first word, and uppercase all of the first letters in each subsequent word. However, in enums, we Capitalize the first letter in every word. If we were to make a variable using “ice cream flavor” we would use iceCreamFlavor if it were an enum we would use IceCreamFlavor.

After that slight stroll through the roses, let’s jump back to the example, enum is similar to func it tells the compiler that we are getting ready to define an enum called Stoplight; we have the open and close curly braces for the body of the enum, and we have cases for each of the options the enum provides.

In a stoplight, we either have a greenlight go, a yellow light slowDown, or a red light stop. You’ll never see a traffic stoplight with more than one light lit up at a time. In the case of this enum, we can use it to provide options that can be used as values or booleans, we will talk about how to use them as values later, but for the scope of this post, we will just cover boolean options.

Let’s see what’s going on here. First, we declare our enum, no news here, then we create a variable named trafficStatus which is of type Stoplight and we initialize it to Stoplight.go. We then set up a boolean to check whether or not 60 seconds have passed. Keep in mind, if we really ran this program it would be finished in a few nanoseconds or less. Next, we set up a counter for the number of seconds passed and set up a variable to hold the time the light changed.

Then we get into the While statement and evaluate
while !(trafficStatus == Stoplight.stop) so we know when to end the program. The first thing we ought to check is if secondsPassed == 60 since we need to set our boolean trafficHasMovedForOneMinute.

Next, we check if trafficHasMovedForOneMinute && trafficStatus != Stoplight.slowDown so when that time comes we can set trafficStatus = Stoplight.slowDown.This also allows us to set the timeLightChanged value to the number of seconds passed. We check to make sure trafficStatus has not already been set so we don’t continually update timeLightChanged.

Then we get down to where we need to check
if trafficStatus == .slowDown and secondsPassed > timeLightChanged before we set trafficStatus = .stop. We can use .slowDown and .stop due to Swift’s type inference. The compiler knows that trafficStatus is of type Stoplight so we can just use the dot operator . to get to whichever case we intend to use.

Finally, we update secondsPassed a second higher for our next run.

So this will loop 60 times before we update trafficHasMovedForOneMinute. Then we immediately change trafficStatus == .slowDown and set the timeLightChanged variable equal to the value held by secondsPassed. Then we skip the last if statement because secondsPassed is not greater than timeLightChanged, they are the same value. We update secondsPassed to 61, then we run through again.

We do not need to update trafficHasMovedForOneMinute because it’s already set to true so we can skip over the first if statement. We skip over the second statement even though trafficHasMovedForOneMinute is set to true because trafficStatus == .slowDown. Finally, we perform our final check
if trafficStatus == .slowDown which is true, and
secondsPassed > timeLightChanged which is also true, and set
trafficStatus = .stop. We update secondsPassed one last time and the while loop starts over again. It immediately realizes “Hey! that traffic light is red” and it exits the loop.

We could extend this example to be more realistic, maybe have trafficLight1, trafficLight2, trafficLight3, and trafficLight4 and have them work in pairs in a never-ending while loop much like normal traffic lights do. We might also add a boolean named carIsPresent to detect whether or not a car has arrived at any of the lights if it has set a variable named carHasBeenWaitingFor30Seconds to force the lights to change early. These could also be broken out into our newly found functions, but I’ll leave that up to you if you want a challenge.

I want to rewind a bit, I told you in the last post that we would cover the dot operator here and I mentioned it in that last explanation. The dot operator . is used to access elements within types. They exist in Strings, Integers, Doubles, Floats, Dictionaries, Arrays, and Booleans. They also exist in Structs and Classes but we aren’t quite there yet.

The dot operator allows you to access functions and variables inside of types. Say What? Here’s an example

Arrays come with built-in methods that allow you to perform work on an array defined by you, myArray.count prints the total number of elements in the array. myArray.reversed() calls a method inside of the Array type that reverses the order of the given array.

A method is just another name for a function inside of an object. For all intents and purposes for this lesson, they do the same thing and look exactly the same. Many developers use the names interchangeably, mistakenly. But almost no one catches the mistake. If you make the mistake, it’s no big deal.

Switch statements and enums play well together, think of a switch statement as a replacement for a really long if statement.

The switch statement is defined using switch <variableToCheck>. We then have an open curly brace to start the body of the switch statement. Instead of writing if trafficLight == .go for each possible condition, we just use
case <enum case>: to define what we are looking for. Swift is smart enough to realize that trafficLight is just a container of the type Stoplight, and can infer that anytime we use a case statement, we are checking for possible values that can be used with trafficLight. The case statement must begin with case, it must have a value .go and must end with a colon :. There are no curly braces used with each case, but you must have a case for every possible option in your enum if you do not use a default: case. The default: case is used for any other condition.

Since Swift can use numbers or ranges of numbers, we can set up a switch statement without an enum. Let’s see how this works in another example using age ranges.

In the example above, we first start off with a half-open range, If the person is less than 0, we print “I am not born yet”. The next option case 0: only checks if the person’s age is 0 if it is we perform the code below. Next, we check the range 1...12 followed by 13 ... 17 each providing their own statement based on the person’s age. If the person is 18 we note they are an adult and able to vote. Since we’ve checked every other possible range, we can use a default: case to cover any other age. Switch statements have a lot more power than just this and as we get into bigger and better things we will go over the other use cases for them.

“Modern art piece featuring a halved orange with the rind painted blue on a blue background” by Cody Davis on Unsplash

Scope

On to the final section, scope. So far we have experienced scope. We experienced it with enums, functions, if statements, while loops, and for loops. In a general sense, we’ve been covering it all along. So what is scope?

Scope defines where an object lives in code.

“OK Bob, you are crazy, you just told me an inanimate object has a life.” No really, in programs, we make them come to life, we make them perform work. Why not consider it this way. Objects in code have a lifespan. For most of what we have been doing since the beginning your variables have been created when the program starts and when the program ends, they go away. That is considered the lifespan of an object.

When you create an object, which is just a general name encompassing things like variables and constants, it has a set lifespan. The lifespan of an object is determined by where it’s placed in the program. If you place an object on its own in a program like var myObject: String = "Hello World" it will last for as long as the program is running. As soon as the program stops, myObject is removed from memory.

The problem is we can’t just load everything into memory. We’d run out of room, so we instead pick and choose what should be available at any given point in a program by defining its scope.

So I’ve talked about what scope is but let’s see some practical examples of how scope can be defined and used in your code and how it makes you think about where to put your variables.

So I’ve defined all kinds of levels of scope in this, let's start from the top.

myGlobalInteger is an object that can be used anywhere in code as long as the program is still running.

myGlobalFunction is a function that can be used anywhere in code as long as the program is still running, however, the name variable inside of the curly braces can only be used inside of the curly braces. As soon as the function exits, the name variable along with its value is removed from memory.

myGlobalReturningFunction is exactly the same as myGlobalFunction except it returns a value, and you might be keen to think it returns the variable created inside of the curly braces because that’s what it returns, but it actually returns only the value of the variable. When the function reaches the final curly brace, the variable name is removed from memory and you just have a copy of it that you hopefully assigned to another variable.

var myName: String? I purposely made this optional but it is still available to every piece of code in the example.

The if statement is interesting though, I used something you haven’t seen before if (expression) == value. First what happens here is we perform the if statement and when we get to the parenthesis we default to the order of operations, we perform the work inside of the parenthesis first.
name = myGlobalReturningFunction then we continue with the rest of the expression. Since we know that the returning function returns "Katy" this if statement would be comparing if "Katy" == "Katy", yes they are the same! So we reference back to the global optional name variable from within the if statement and grab the value from there (because we assigned a value when we performed the evaluation).

However, let's say the name returned was something else like "Sophie" which is not the same as "Katy". In the else statement there is both a constant and a variable that is created and initialized. They can only be used while we are in curly braces of the else statement. If we were to have a variable created in the if portion of the if statement, it could only be used in the if portion.

We can still print the myGlobalVariable andmyglobalFunction, because they were defined outside of the curly braces.

MyEnum is similar in that the cases can only be used from within MyEnum, as in MyEnum.hasCoffee. We cannot use .hasCoffee on its own unless the type is inferred because it would be out of scope. By type inference I mean
var myCup: MyEnum knows that the only value it should have is something from MyEnum so when we make it equal to .hasCoffee. The compiler just writes it out for you so it is var myCup = MyEnum.hasCoffee.

If you forget everything else I said, just know that you can always go deeper into curly braces with a variable, but you can never go outside of the curly braces where the variable was initially declared.

Just imagine that whenever you leave the curly braces that contain a variable, Bones says “He’s dead, Jim!” Not much of a Star Trek fan? Well, I know a lot of people that like Hotel California and it works on much the same premise, you can never leave. Not an Eagles fan either, well I’m sure you can come up with your own mnemonic to help you remember how scope works if you feel like you need one.

And that is scope.

Summary

So today, with the help of tangled shoelaces, we realized the code we have written so far is a bit sloppy.

We started off with 3 principles to help us imagine how our code should look, Separation of Concerns, the Single Responsibility Principle, and Don’t Repeat Yourself.

We then talked about Functions and how they can help us abide by these principles. I strongly recommend you practice writing your own functions so you get the hang of it and understand what is and is not allowed. We also learned about how to exit early from functions using return.

We moved on to Enums because they are neat and give you more than just true and false. They are useful for other things but we will get into other uses later.

Finally, we talked about Scope. Scope is very important to your code, it’s how most bugs come about in your code, I’ve seen a lot of questions about bugs in code that have come up because the scope of the variable was not defined correctly. Just a tip, declare your variables at the lowest scope they will be used, then as you need it available in other places, move the declaration to a higher scope. As we move on this will become more apparent in how we do this. I’m sure my future examples will do this.

What’s Next

You made it! Up next we will be talking about Structs, Classes, Properties, and Methods.

Woah, woah, woah Bob! All we’ve been doing so far is just 2 or 3 topics at a time, what’s up with 4? Truth be told, if you’ve been following along you already know properties and methods. I just need to introduce you to Structs and Classes first.

In the next part, we will learn about what Classes and Structs are, how they are similar, how they are different, and when you should use one over the other.

--

--

Bob Roebling
Swift2Go

Bob is a Senior Infrastructure Administrator and tech evangelist with a background in multiple programming languages.