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
Swift Essentials

You're reading from   Swift Essentials Get up and running lightning fast with this practical guide to building applications with Swift

Arrow left icon
Product type Paperback
Published in Dec 2014
Publisher
ISBN-13 9781784396701
Length 228 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (2):
Arrow left icon
Bandlem Limited Bandlem Limited
Author Profile Icon Bandlem Limited
Bandlem Limited
Alex Blewitt Alex Blewitt
Author Profile Icon Alex Blewitt
Alex Blewitt
Arrow right icon
View More author details
Toc

Functions

Functions can be created using the func keyword, which takes a set of arguments and a body that can be invoked. The return statement can be used to leave a function:

> var shopping = [ "Milk", "Eggs", "Coffee", "Tea", ]
> var costs = [ "Milk":1, "Eggs":2, "Coffee":3, "Tea":4, ]
> func costOf(items:[String], costs:[String:Int]) -> Int {
.   var cost = 0
.   for item in items {
.    if let cm = costs[item] {
.     cost += cm
.    }
.   }
.   return cost
. }
> costOf(shopping,costs)
$R0: Int = 10

The return type of the function is specified after the arguments with an arrow (->). If missing, the function cannot return a value; if present, the function must return a value of that type.

Functions with positional arguments can be called with parentheses, such as the costOf(shopping,costs) call. If a function takes no arguments, then the parentheses are still required.

Note

The foo() expression calls the function foo with no arguments. The expression foo is the function itself, so an expression such as let copyOfFoo = foo results in a copy of the function; so copyOfFoo() and foo() have the same effect.

Named arguments

Swift also supports named arguments, which can either use the name of the variable or can be defined with an external parameter name. To modify the function to support calling with basket and prices as argument names, the following can be done:

> func costOf(basket items:[String], prices costs:[String:Int]) -> Int {
.   var cost = 0
.   for item in items {
.    if let cm = costs[item] {
.     cost += cm
.    }
.   }
.   return cost
. }
> costOf(basket:shopping, prices:costs)
$R1: Int = 10

This example defines external parameter names basket and prices for the function. The function signature is often referred to as costOf(basket:prices:) and is useful when it may not be clear what the arguments are for (particularly if they are for the same type).

A shorthand is available to use the same external name as the parameter name, by prefixing it with a hash (#). These are called shorthand external parameter names:

> func costOf(#items:[String], #costs:[String:Int]) -> Int {
. var cost = 0
. for item in items {
.  if let cm = costs[item] {
.   cost += cm
.  }
. }
. return cost
. }
> costOf(items:shopping, costs:costs)
$R2: Int = 10

Refactoring shorthand external parameter names will lead to API breakage. If it is necessary to change the name internally in a function, convert it from a shorthand name to a separate external and internal parameter name.

Optional arguments and default values

Swift functions can have optional arguments by specifying default values in the function definition. When the function is called and an optional argument is missing, the default value for that argument is used.

Note

Note that an optional argument is one that can be omitted in the function call, rather than a required argument that takes an optional value. This naming is unfortunate. It may help to think of these as default arguments rather than optional arguments.

A default parameter value is specified after the type in the function signature, with an equal sign (=) and then the expression. The expression is re-evaluated each time the function is called without a corresponding value. Default arguments are implicitly named so that the hash (indicating a named argument) is superfluous and will generate warnings.

In the costOf example, instead of passing the value of costs each time, it could be defined with a default parameter as follows:

> func costOf(#items:[String], costs:[String:Int] = costs) -> Int {
.   var cost = 0
.   for item in items {
.    if let cm = costs[item] {
.     cost += cm
.    }
.   }
.   return cost
. }
> costOf(items:shopping)
$R3: Int = 10
> costOf(items:shopping, costs:costs)
$R4: Int = 10

Note that in the first expression, the captured costs variable is bound when the function is defined. If costs is re-assigned at a later stage, then the function will not be updated.

Anonymous arguments

Swift requires that arguments with default values are named, as are arguments that are used in initializers for classes (which are covered in the Classes in Swift section in Chapter 3, Creating an iOS Swift App).

In some cases, this is unnecessary or unhelpful. To disable requiring a named argument for a parameter, the special value underscore (_) can be used:

> func costOf(items:[String], _ costs:[String:Int] = costs) -> Int {
.   var cost = 0
.   for item in items {
.     if let cm = costs[item] {
.       cost += cm
.     }
.   }
.   return cost
. }
> costOf(shopping)
$R0: Int = 10
> costOf(shopping,costs)
$R1: Int = 10

Multiple return values and arguments

So far, the examples of functions have all returned a single type. What happens if there is more than one return result from a function? In an object-oriented language, the answer is to return a class; however, Swift has tuples, which can be used to return multiple values. The type of a tuple is the type of its constituent parts:

> var pair = (1,2)
pair: (Int, Int) ...

This can be used to return multiple values from the function; instead of just returning one value, it is possible to return a tuple of values.

Note

Swift also has in-out arguments, which will be seen in the Handling Errors section in Chapter 6, Parsing Networked Data.

Separately, it is also possible to take a variable number of arguments. A function can easily take an array of values with [], but Swift provides a mechanism to allow calling with multiple arguments, using variadic functions. The last argument in a function signature can be variadic, which means that it has ellipses after the type. The value can then be used as an array in the function.

Taken together, these two features allow the creation of a minmax function, which returns both the minimum and maximum from a list of integers:

> func minmax(numbers:Int...) -> (Int,Int) {
.   var min = Int.max
.   var max = Int.min
.   for number in numbers {
.    if number < min {
.     min = number
.    }
.    if number > max {
.     max = number
.    }
.   }
.   return(min,max)
. }
> minmax(1,2,3,4)
$R0: (Int, Int) = {
  0 = 1
  1 = 4
}

The numbers:Int... indicates that a variable number of arguments can be passed into the function. Inside the function, it is processed as an ordinary array; in this case, iterating through using a for...in loop.

Note

The Int.max constant represents the largest Int value, and Int.min is a constant representing the smallest Int value. Similar constants exist for specific integral types, such as UInt8.max and Int64.min.

What if no arguments are passed in? If run on a 64 bit system, then the output will be as follows:

> minmax()
$R1: (Int, Int) = {
  0 = 9223372036854775807
  1 = -9223372036854775808
}

This may not make sense for a minmax function. Instead of returning an error value or a default value, the type system can be used. By making the tuple optional, it is possible to return a nil value if it doesn't exist, or a tuple if it does:

> func minmax(numbers:Int...) -> (Int,Int)? {
.   var min = Int.max
.   var max = Int.min
.   if numbers.count == 0 {
.     return nil
.   } else {
.    for number in numbers {
.     if number < min {
.      min = number
.     } 
.     if number > max {
.      max = number
.     }
.   }
.   return(min,max)
. }
. }
> minmax()
$R2: (Int, Int)? = nil
> mimmax(1,2,3,4)
$R3: (Int, Int)? = (0 = 1, 1 = 3)
> var (minimum,maximum) = minmax(1,2,3,4)!
minimum: Int = 1
maximum: Int = 4

Returning an optional value allows the caller to determine what should happen in cases where the maximum and minimum values are not present.

Tip

If a function does not always have a valid return value, use an optional type to encode that possibility into the type system.

Returning structured values

A tuple is an ordered set of data. The entries in the tuple are ordered, but it can quickly become unclear as to what data is stored, particularly if they are of the same type. In the minmax tuple, it is unclear which value is the minimum and which is the maximum, and this can lead to subtle programming errors later on.

A structure is like a tuple, but with named values. This allows members to be accessed by name instead of by position, leading to fewer errors and greater transparency. Named values can be added to tuples as well. In essence, tuples with named values are anonymous structures.

Tip

Structs are passed in a copy-by-value manner, like tuples. If two variables are assigned the same struct or tuple, then changes to one do not affect the value of another.

A struct is defined with the keyword struct and has variables or values in the body:

> struct MinMax {
.   var min:Int
.   var max:Int
. }

This defines a MinMax type, which can be used in place of any of the types seen so far. It can be used in the minmax function to return a struct instead of a tuple:

> func minmax(numbers:Int…) -> MinMax? {
.   var minmax = MinMax(min:Int.max, max:Int.min)
.   if numbers.count == 0 {
.     return nil
.   } else {
.     for number in numbers {
.       if number < minmax.min {
.         minmax.min = number
.        }
.       if number > minmax.max {
.         minmax.max = number
.       }
.     }
.     return minmax
.   }
. }

The struct is initialized with a type constructor; if MinMax() is used, then the default values for each of the structure members are used (based on the structure definition), but these defaults can be overridden explicitly if desired, with MinMax(min:-10,max:11). For example, if the MinMax struct is defined as struct MinMax { var min:Int = Int.max; var max:Int = Int.min }, then MinMax() would return a structure with the appropriate maximum and minimum values filled in.

Note

When a structure is initialized, all the fields must be assigned. They can be passed in as named arguments in the initializer, or specified in the structure definition.

Swift also has classes; these are covered in the Swift classes section in the next chapter.

lock icon The rest of the chapter is locked
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