Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Learn Swift by Building Applications

You're reading from   Learn Swift by Building Applications Explore Swift programming through iOS app development

Arrow left icon
Product type Paperback
Published in May 2018
Publisher Packt
ISBN-13 9781786463920
Length 366 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Giordano Scalzo Giordano Scalzo
Author Profile Icon Giordano Scalzo
Giordano Scalzo
Emil Atanasov Emil Atanasov
Author Profile Icon Emil Atanasov
Emil Atanasov
Arrow right icon
View More author details
Toc

Functions

In this section, we will learn how to define functions and how to use them in our code. They help us to reuse sequences of statements with ease. We can define a general solution of a problem and then apply it, customized, to different parts of our app. This approach saves time, reduces the potential of bugs in the code, and simplifies huge problems to small ones.

The first function, which we already saw in use, is print(). It's used to display text on the screen. We will experiment with this in the next chapter, once we get our hands dirty with Xcode and Swift.

Now let's define our first function, which executes a sequence of statements in its body:

func printSum() {
let a = 3
let b = 4
print("Sum \(a) + \(b) = \(a + b)")
}

When defining a function, we start with the special word func. Then the name of the function follows and the list of the arguments in brackets ( ) and its returned type. After that comes the body of the function in curly braces { }.

The name can start with any letter or underscore and can be followed by a letter, digit, underscore, or dollar sign. A function name shouldn't match any keyword from the Swift language.

This definition doesn't do anything if we don't call (execute) the function. How is this done?

We have to call the function using its own name as follows:

printSum()

Once a function is called, we may think that the same sequence of code is executed where the function call is made. It's not exactly the same, but we can think of having the body of the function executed line by line.

Now let's see the general form of a function:

func functionName(argumentLabel variableName:String) -> String {
let returnedValue = variableName + " was passed"
return returnedValue
}
//here is the function invocation and how the result is returned
let resultOfFunctionCall = functionName(argumentLabel: "Nothing")

Each function may have no arguments, one argument, or many arguments. Until now, we have seen some without arguments and with a single argument. Every argument has an argument label and a parameter name. The argument label is used when the function is called. This is really useful when we have many parameters. It gives us a clue what data should be passed to that specific parameter when using the function. The parameter name (variable name) is the name which will be used in the function body to refer to the passed value. All parameters should have unique parameter names; otherwise there is ambiguity, and we won't be able to say which one is which.

A function may return a value from a specific type, or it may be void (nothing will be returned). When we want to return something, we have to define that, and this is done with -> and the type of the result. In the preceding code, we see -> String, and this means that the function returns a value of the String type. This obliges/binds us to using the keyword return in the function body at least once. The return keyword immediately stops the execution of the function and returns the value passed. If a function doesn't return anything, we can still use return in its body, and it will work similarly to break in a loop.

We can use _ if we want to skip the label of an argument. Here is a simple piece of code that illustrates that:

func concatenateStrings(_ s1:String, _ s2:String) -> String {
return s1 + s2
}
let helloSwift = concatenateStrings("Hello ", "Swift!")
// or
concatenateStrings("Hello ", "Swift!")

When we don't use the _ (underscore), then the argument name is the same as the parameter name (variable name).

Similar to what we have seen with the labels, we can ignore the returned value once the function is called.

What happens if we want to return multiple values? We can use tuples to return multiple values when executing a function. The following code is an example of this:

//define a function which finds the max element and its index in an 
array of integers
func maxItemIndex(numbers:[Int]) -> (item:Int, index:Int) {
var index = -1
var max = Int.min
//use this fancy notation to attach an index to each item
for (i, val) in numbers.enumerated() {
if max < val {
max = val
index = i
}
}

return (max, index)
}

let maxItemTuple = maxItemIndex(numbers: [12, 2, 6, 3, 4, 5, 2, 10])
if maxItemTuple.index >= 0 {
print("Max item is \(maxItemTuple.item).")
}
//prints "Max item is 12."

What is a tuple?

A tuple is a bundle of different types (they may be the same) which have short names. In the preceding code, we have a tuple of two Int statements. The first one is named item, and the second one is named index. After the execution of the function, we will store the maximum item and its index in the tuple. If there are no items in the array, then the index will be -1.

It's possible to return an optional tuple type if there is a chance to return nil in some cases. The previous function may return nil if there are no items, and a valid result otherwise.

Each parameter may have a default value set. To set a default value, you have to declare it and add it right after the parameter's type. The following code is an example of this:

func generateGreeting(greet:String, thing:String = "world") -> String {
return greet + thing + "!"
}

print(generateGreeting(greet: "Hello "))
print(generateGreeting(greet: "Hello ", thing: " Swift 4"))

We can easily define a function which accepts zero or more variables of a specified type. This is called a variadic parameter. Each function definition could have, at most, one variadic parameter. It's denoted with ... after its type. In the body of the function, the type of this parameter is converted to an array. This array contains all passed values:

func maxValue(_ numbers:Int...) -> Int {
var max = Int.min
for v in numbers {
if max < v {
max = v
}
}

return max
}

print(maxValue(1, 2, 3, 4, 5))
//prints 5

One specific thing that we should know about function parameters is that they are constants. We can't mutate these by mistake. We should express this explicitly. To do so, we have to use the special word inout to mark the parameter. The inout parameters is added before the type of the parameter. We can pass variables to the inout parameters, but we can't pass constants. To pass a variable, we should mark this with & when calling the function. The inout parameters can't have default values. Also, variadic parameters can't be marked as such. In general, we can use the inout parameters to return values from a function, but this is not the same as returning values using return. This is an alternative way to let a function affect the outer world in the matrix. Check out the following code:

func updateVar(_ x: inout Int, newValue: Int = 5) {
x = newValue
}

var ten = 10
print(ten)
updateVar(&ten, newValue: 15)
print(ten)

What is the guard statement?

The guard statement has similar behavior to an if statement. This statement checks the condition, and if it's not met, then the else clause is triggered. In the else clause, the developer should finish the current function or program, because the prerequisites won't be met. Take a look at the following code:

func generateGreeting(_ greeting: String?) -> String {
guard let greeting = greeting else {
//there is no greeting, we return something and finish
return "No greeting :("
}
//there is a greeting and we generate a greeting message
return greeting + " Swift 4!"
}

print(generateGreeting(nil))
print(generateGreeting("Hey"))

This is a tiny example, showing us code that illustrates the regular usage of the guard statement. We can combine it with the where clause, or we can make the check really complex. Usually, it's used when the code depends on several if...let checks. The guard statement keeps the code concise.

How to tackle huge problems – bottom-up versus top-down

Step-by-step through this book, we will start solving problems until we can write a fully-working mobile app. It's not an easy task, but we can take two different approaches when trying to solve a huge problem (such as writing a mobile app). The first one is top-down. This technique starts from the top with the main problem, and breaks it down into smaller problems and functions. Once we reach something unclear, something which is not well defined that we should implement, then we define a new function, but we won't continue developing the exact implementation of this part of the app immediately. Let's assume that we are trying to develop a mobile app with three screens. The first one displays a list of news. The second one renders specific news, and the last one shows information about our application.

If we apply the top-down approach, then we will have the following abstract process. We start from the biggest problem: how to develop an app with three screens. Then, we break this huge task down into three sub-tasks with their respective functions. Those functions are empty functions. The first one will be responsible for creating the first screen, the second one should create the detailed news presentation, and the third should define the last screen. By doing this, we have decomposed the main problem into three smaller ones. These new functions are empty, but at a later phase we will implement each one of them. We can start with the first one: we define another help function which creates the list of news, and another function which fetches the news from an internet address. Now it doesn't look really hard to define those functions. We will learn how to do this throughout the book, but the general idea is to break down each problem into smaller ones until you reach a state where you can solve them without any hassle. In the end, the main problem will be solved, because all parts that have been decomposed are already working, and the final result will be a fully-working mobile application.

The other approach is bottom-up, which does things in reverse. It's more like working with Lego, but you first go and build many small building blocks, which you combine together until you manage to build a solution to the whole problem; in our case, until you build a working mobile app. Abstractly, we develop simple enough functions that we can implement to solve small problems. Then we combine those into bigger chunks. Those bigger chunks are put together in even bigger and more complex functions or app parts, until we define the final working app.

Neither of these two approaches is the best. Every developer prefers to use a nice mixture of both techniques, which leads to the final result—a working app.


If top-down, or bottom-up, is used on its own, it is not a silver bullet. Try to use top-down and bottom-up together and you will find the solution easier.

Just tweak your approach based on what you know at the moment, and what you have.

You have been reading a chapter from
Learn Swift by Building Applications
Published in: May 2018
Publisher: Packt
ISBN-13: 9781786463920
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image