Functions and Methods - OBJECT-ORIENTED PROGRAMMING - Understanding Swift Programming: Swift 2 (2015)

Understanding Swift Programming: Swift 2 (2015)

PART 2: OBJECT-ORIENTED PROGRAMMING

13. Functions and Methods

Functions, according to Apple, are "self-contained chunks of code that perform a specific task."

This definition is very much the same as what Apple uses for another entity in Swift, that of a closure. Functions and their siblings, closure expressions, are actually both specific cases of closures. The main difference is that functions have names while closure expressions do not, and each has its own syntax.

This chapter describes the fundamentals of functions and of methods. The latter are functions associated with, and defined within, a class (or structure or enumeration).

A later chapter, Chapter 17, "Functions Revisited: First Class Citizens" will describe how functions in Swift can be assigned to variables, passed as input arguments to other functions, and returned from functions. The chapter also describes how functions can be nested within other functions and how nested inner functions can capture variables and the like and be passed to a global environment and then executed.

Another chapter, Chapter 18, "Closures and Closure Expressions", will describe closures and closure expressions, and how functions are related to these.

Still another chapter, Chapter 22, "Functions Revisited Again: Input Parameters", will describe the wide variety of input parameter syntax that is allowed for functions.

The chunk of code that is in the body of a function is given a name, which is used to call the function when the task is to be performed, and the function has a syntax that allows for passing the values of parameters to the code and for the code to return a value. The general syntax of a function is shown in the figure below:

New in Swift 2, there is no difference between the syntax of a method and that of a function. The preferred form is to name it so that the name of the function or method suggests what the first parameter is. Starting with the second parameter, all parameters must have external names.

A typical example of a Swift function is:

func addTwoNumbersWithFirst (first: Int, second: Int) -> Int {

var c = first + second

return c

}

This function, which adds together two integers, is called as follows:

var d = addTwoNumbersWithFirst(2, second: 5)

print(d) // Prints: 7

The syntax of a function includes the keyword func followed by the name of the function. Input parameters, if used, are contained within parentheses. The parentheses are required even if there are no input parameters.

This approach in Swift of calling functions and methods using external parameter names is pretty clearly motivated by the way that methods are named and parameters passed in Objective-C. Here's an example of the addTwoNumbers method as it might be called in Objective-C:

int d = [math addTwoNumbersWithFirst: 2 second: 5] // Objective-C syntax, not Swift

The important part of this Objective-C method call is the addTwoNumbersWithFirst: 2 second: 5 part of it.

Methods in Objective-C are typically named so that the method name makes sense as a name not only for the method but for the first parameter. (This sometimes results in some fairly strange method names.) Any additional parameters have names just for those parameters. Thus we haveaddTwoNumbersWithFirst, a single name that both calls the method and provides a name for the first parameter, which comes just after a colon following the method name. A space follows, then a second parameter name, a colon, and a second parameter.

The idea is that this makes the method call readable as something like English: "add two numbers, with the first being 2 and the second being 5".

If you aren't familiar with calling methods with external parameter names, this may seem pretty strange. But they make code very readable, and, in particular, they prevent errors resulting from mixing the order of the argument values. (This isn't so readily seen in the example, because obviously it makes no difference when adding numbers which one comes first, but there are many calculations where it is important which value goes first.) Note that when you use parameters with external names, you still have to provide the arguments in the correct order—the names are just to make it easy for you to do this.

This is arguably a somewhat clumsy aspect of the Swift language definition. The Objective-C version always has argument values coming right after a method or parameter name, while the first argument value in the Swift version comes after a left parentheses. This is something programmers have to get used to. But the named parameters are a nice aspect of Objective-C, and it is useful for Swift to use some form of it.

(In Swift 1, external names are not required, and cannot be used, in a function unless specifically defined in the function.)

There are many variations for the syntax for input parameters. In the variation shown here, each input parameter has a name for the parameter, used to refer to it within the function, and the type of the parameter. This “local” name is also used in the function call, but only for the 2nd and subsequent parameters (and only in Swift 2).

The most unusual aspect of the syntax comes right after the input parameters and before the left brace indicating the beginning of the body of the function. We have a "->" character sequence (known as a return arrow), followed by a type. The latter indicates the data type of the return value of the function (if there is one).

This is quite different from the syntax of a function in C. In C there is no func keyword, there is no ->, and there are no external parameter names.

I've shown this same function below, together with what it would look like if coded in C, to easily compare the two:

func addTwoNumbersWithFirst (first: Int, second: Int) -> Int { // Swift version

var c = first + second

return c

}

int addTwoNumbers (int a, int b) {

// C version

var c = a + b;

return c;

}

Both are called in a similar way, although (in Swift 2) names are used for the second and subsequent parameters.

No Input Parameters or No Return Value

It is often the case that you have no input parameters—you just want the function to do something. In this case there are no specified parameters but a () is included in both the function definition and the call:

func takeAction () -> Bool {

// Do something and get a response

let response = true // Worked ok

return response

}

let m = takeAction()

Sometimes you want to do something and not even provide a response. There is no return value. In this case both the return arrow (->) and the type after it are omitted:

takeAction2 () {

// Do something and do not provide a response

}

takeAction2()

Functions That Return Multiple Values

We can use a tuple to return multiple values from a function. We saw this briefly in the chapter on tuples; you can now see it again with more knowledge about what functions are supposed to look like:

func getErrorCodeAndText (a: Int) -> (Int, String) {

var errorCodeAndText:(Int, String) = (0,"") // Init

if(a == 0) {

errorCodeAndText.0 = 111

errorCodeAndText.1 = "Error: Disk out of space."

}

else {

errorCodeAndText.0 = 333

errorCodeAndText.1 = "Error: Disk not in drive."

}

return errorCodeAndText

}

var d = getErrorCodeAndText(0)

print("Error code is \(d.0) and Message is \(d.1)")

The type for the return value is (Int, String). That is, it is a compound type that is a tuple that includes values of Int and String.

Methods

A method, as discussed earlier, is simply a function that is associated with, and defined within, a class, structure, or enumeration. In Swift 2, the syntax for a method is exactly the same as it is for a function. There are two kinds of methods: instance methods, and class or type methods.

INSTANCE METHODS

An instance method is associated with a particular instance of a class, and is called from that instance. It can access the values of the stored properties of classes and structures that are associated with that instance.

Suppose we have a class called Dog that has three instance properties: numberOfFirstPrizeRibbons, numberOfSecondPrizeRibbons, and totalNumberOfPrizeRibbons.

We might create an instance of Dog that represents a particular dog:

var particularDog = Dog()

particularDog.numberOfFirstPrizeRibbons = 3

particularDog.numberOfSecondPrizeRibbons = 2

And, assuming that we have defined the method addTwoNumbersWithFirst:second: within the class definition of Dog, we can use it to calculate the value of the property totalNumberOfPrizeRibbons:

particularDog.totalNumberOfPrizeRibbons = particularDog.addTwoNumbersWithFirst(particularDog.numberOfFirstPrizeRibbons, second: particularDog.numberOfSecondPrizeRibbons)

Note how we call a method: we use the variable or constant that has a reference to the instance, a period, the name of the method, and then the input parameters enclosed in parentheses.

CLASS OR TYPE METHODS

Class (type) methods are defined with the same syntax as instance methods. However, they cannot use instance properties, but only global variables and constants and class (type) properties.

They are called from the name of the class (or structure or enumeration) rather than from an instance:

var a = 2

var b = 5

var c = Dog.addTwoNumbersWithFirst(a, second: b)

(This assumes that we included the method addTwoNumbersWithFirst: Second: in the definition of the class Dog, and marked it with the class keyword.)

Avoiding the Use of Named Parameters

You can change the default behavior that requires named parameters with an underscore ("_") character. For example, if you place an underscore just before the name of the second parameter, you don't need to specify the parameter name in the call, and, if you do, you will get a compiler error:

func addTwoNumbersWithFirst (first: Int, _ second: Int) -> Int {

var c = first + second

return c

}

var d = addTwoNumbersWithFirst(2, 5) // Works

var d = addTwoNumbersWithFirst(2, second: 5) // Compiler error

Scoping of Variables and Constants in Functions and Methods

Swift follows conventional C rules for the scoping of variables (and constants). Variables that are declared within a function are local to that function and cannot be accessed outside of that function.

Variables (and constants) that are declared at a global scope that is at the same scope of a function, or higher, can be accessed from within that function.

Variables (and constants) that are declared as properties in a class or structure can be accessed from any methods (functions) that are defined within that class or structure. (Class or type methods can only access class or type properties, not instance properties.)

Hands-On Exercises

Go to the following web address with a Macintosh or Windows PC to do the Hands-On Exercises.

For Chapter 13 exercises, go to:

understandingswiftprogramming.com/13