© Radoslava Leseva Adams and Hristo Lesev 2016

Radoslava Leseva Adams and Hristo Lesev, Migrating to Swift from Flash and ActionScript, 10.1007/978-1-4842-1666-8_19

19. Types

Radoslava Leseva Adams and Hristo Lesev2

(1)London, UK

(2)Kazanlak, Bulgaria

Prepare for a relatively fast pace in this chapter. We will speed through topics related to types in Swift and delve in more detail into those that are likely to be less familiar to an ActionScript developer. The chapter starts with an overview of Swift’s type policy and goes briefly over some of its primitive types. Then we go a little deeper and explore optional types. Expect weird syntax there. Moving on, we see how to query an object’s type and how to type cast. We finish with a few useful references to container types, tuples, and function types. Seatbelt ready?

Type Safety and Type Inference

Swift is a type-safe language: its compiler performs type checks and lets you know if there are any mismatched types. Making sure to provide a type for everything you declare has two advantages: your code and intentions become self-documented and you have more confidence at compile time that your code will do what you intend it to. It, however, has a couple of disadvantages too: having to add types everywhere can become a chore, and if you have complicated types the result is rather verbose code.

In order to help with this, the Swift compiler can infer types from values assigned to variables and constants. This applies to literal values, as well as values that result from expressions. Thus you have a choice regarding when to explicitly add types to your declarations and can afford conciseness.

A good guideline for when to rely on the compiler and when to be explicit is to write code for humans1: omit the type declarations when they are obvious and add them when they provide useful information to the reader.

In order to understand what the compiler will do when a literal is assigned to a type-inferred variable, it is good to know the types of literals. For example, literal integer values are inferred to be Int, rather than UInt, and floating-point values are inferred to be Double by default, rather than Float (Listing 19-1).

Listing 19-1. Inferring Types from Literal Values: The Compiler Defaults to Int and Double
var age = 64 // age is inferred to be Int, rather than UInt                                    
var height = 1.75 // height is inferred to be Double, rather than Float

Primitive Types

As you come from an ActionScript programming background, primitive types in Swift will be familiar in how they behave for the most part. And, as with most things in Swift, the devil is in the detail: you will find some subtle, but in some cases quite important, differences. Curious? Read on.

Integers

You have at your disposal various flavors of signed and unsigned integer types, each conveniently having a self-explanatory name: Uint8 stands for “unsigned 8-bit integer,” Int32 for “signed 32-bit integer,” for example. Unless you need a specific size for your integer data, most of the time it is prudent to use the Int and UInt type aliases, which have the same size as the platform’s native word. You can find the valid range for any integer type by calling its min and max properties—for example, let maxInt = Int.max.

When you work with integer literals you can use decimal, binary, octal, or hexadecimal notation, as Listing 19-2 demonstrates. All notations, except the decimal one, require a prefix: 0b for binary, 0o for octal, and 0x for hexadecimal.

Listing 19-2. Integer Literals in Different Notations
// Representations of the number 42 in different notations:
let decimalInt = 42              // no prefix required
let binaryInt = 0b101010         // uses the 0b prefix
let octalInt = 0o52              // uses the 0o prefix
let hexadecimalInt = 0x2A        // uses the 0x prefix

Floating-Point Types

With floating-point values you have a choice between Double, which takes 64 bits, and Float, which takes 32 bits. It is worth noting that as with a lot of things in Swift, you are expected to be explicit:

  • A floating-point literal will be inferred to be of type Double, unless you explicitly cast it to Float.

  • You can’t omit the zero in a floating-point literal. If you do, the compiler issues an error: .5 is not a valid floating point literal; it must be written '0.5'.

  • You can’t mix numbers of different types in mathematical operations: Swift doesn’t do type coercion behind the scenes. This applies to all numeric types.

Listing 19-3 illustrates these points with a few examples.

Listing 19-3. You Need to Be Explicit with Swift Types
// Using .5 instead of 0.5 generates a compiler error:
let doubleNumber = 0.5


// floatNumber is inferred to be of type Double by default,
// unless you explicitly cast it to Float:
let floatNumber = 0.2 as Float


// This line does not compile,
// unless you explicitly convert floatNumber to Double:
let difference = doubleNumber - Double(floatNumber)

Booleans

The Boolean type in Swift is named Bool and the constant values it takes are true and false, just as in ActionScript. Unlike in ActionScript, however, other types can’t masquerade as Booleans—all in the name of being explicit, as we saw with the numeric types above. For example, the if statement in Listing 19-4 will not compile: the literal Int value provided in place of the condition will not be automatically coerced to Bool.

Listing 19-4. If Statements Take Only Bool Conditions
if 1 {
    print("Gotcha!")
}
// error: type 'Int' does not conform to protocol 'BooleanType'

Characters

Swift has a special type, called Character, which represents a single human-readable symbol. You can initialize a Character with a literal symbol in double quotes. Note, however, that such literals are of type String. So, in order to create a Character instance, you need to either provide a type explicitly or type cast the literal, as the example in Listing 19-5 shows.

A371202_1_En_19_Figa_HTML.jpg
Listing 19-5. Creating and Initializing a Character

There are a couple of details worth noting about Character instances in Swift:

  • They don’t always take the same amount of memory.

  • The same symbol can be represented in more than one way behind the scenes.

Why is that? Characters in Swift are Unicode-compliant and are represented by Extended Grapheme Clusters. These are clusters of Unicode codes, called Unicode Scalars, each of which stands for a character or for a character modifier. When you want to output a letter with the umlaut modifier, for example ä, you have a couple of options for how it will be stored in memory: it can be represented by a single Unicode Scalar that stands for a with umlaut (U+E4) or by a combination of the code for a (U+61) and the code for umlaut (U+308). Listing 19-6 shows how you can explicitly initialize a Character with an Extended Grapheme Cluster.

Listing 19-6. Initializing Characters with Extended Grapheme Clusters
let aWithUmlaut : Character = "u{E4}"
print(aWithUmlaut)
// output: ä


let aWithUmlautModifier : Character = "u{61}u{308}"
print(aWithUmlautModifier)
// output: ä

These two facts (characters’ sizes in memory can vary, and there can be more than one way of encoding the same symbol) are very important and have the following implications:

  • When you compare two characters for equality, using the == or the != operator, the comparison is not done on the Unicode Scalars that they store but on the symbols that the Unicode Scalars result in. In other words, the two constants in Listing 19-6, aWithUmlaut and aWithUmlautModifier, will be considered equal, although they look different in memory.

  • Finding out the length of a character array or a string is not a matter of dividing the number of bytes in the array or string by the size of a character. Instead an iteration is done over the whole array or string to find out and add up the variable sizes of its characters.

  • The characters in a string can’t be indexed using numbers. That is, you can’t do myString[0] or myString.charAt(0) to get to the first character of the string. Instead, you need to iterate over the characters from the start or from the end of the string to get to the one you need to access.

Strings

Strings in Swift consist of zero or more Unicode-compliant characters. In the next subsection we will see how strings are instantiated, initialized, and modified and will mention some pitfalls it is a good idea to be aware of.

Initializing Strings

You can use a string literal enclosed in double quotes, or you can provide an array of Character instances, in order to initialize a String (see Listing 19-7).

A371202_1_En_19_Figb_HTML.jpg
Listing 19-7. Ways of Creating and Initializing a String

You can also use combinations of other String and Character variables or constants to create a new String. Listing 19-8 uses the String and Character constants from the previous example to show you a couple of different ways of concatenating strings and characters: using the + or the += operators and the append method of the String class.

A371202_1_En_19_Figc_HTML.jpg
Listing 19-8. Concatenating Strings

Note that helloDialog in the listing is declared as a variable, rather than a constant. This allows you to modify both its length and its contents.

You can compare strings with the equality (==) and the inequality (!=) operator or by using one of the flavors of the String class compare method. Bear in mind that string comparison is a potentially resource-heavy operation, as we discussed earlier in the section “Characters.”

The characters method of the String class gives you access to the characters of the String as an array. Listing 19-9 shows a situation you might find a bit surprising: you can call methods even on literals.

Listing 19-9. Enumerating the Characters in a String
for ch in "Strange syntax or what?!".characters {
    print(ch)
}


// output:
//S
//t
//r
//a
//n
//g
//e
//
//s
//y
//n
//t
//a
//x
//
//o
//r
//
//w
//h
//a
//t
//?
//!

Inserting Values into Strings

Something that you will probably find yourself doing a lot is string interpolation: inserting values of constants or variables into strings, also known as string templating. To insert a value into a string, wrap it with parentheses and prefix it with a backslash: (value). Listing 19-10 demonstrates how to insert string and numeric values into a string literal. It also shows how you can convert an integer to its hexadecimal string representation and uses an escape sequence to insert a new line ( ) in the middle of the printed sentence. In Swift you can use all escape sequences you are familiar with from ActionScript; the only exception is the sequence for escaping a Unicode Scalar, which has a slightly different syntax, as you saw earlier in Listing 19-6, when we discussed characters.

Listing 19-10. Interpolating Values in Strings
// Get the UnicodeScalar for the symbol ä:
let aWithUmlautCode = UnicodeScalar("ä")


// Next, get the UnicodeScalar integer value
// and store its hexadecimal representation into a String:
let hexCode = String(aWithUmlautCode.value, radix: 16)


// Last, print it out, inserting a new line ( ) in the middle of the sentence:
print("The Unicode Scalar for (aWithUmlautCode) is (hexCode),
       or (aWithUmlautCode.value) in decimal notation.")


// output:
// The Unicode Scalar for ä is e4,
//  or 228 in decimal notation.

Inadvertently Copying Strings

Let us close the topic on strings with a word of caution. Unlike in ActionScript, where String instances are passed by reference2 when assigned to other strings or passed to functions, in Swift their default behavior is to be passed by value. In other words, a String instance that is assigned to another variable or passed as an argument to a function might3 end up being copied multiple times, which can be costly.

Optionals

If you have tried pretty much any Swift tutorial, you may have wondered what all those question marks and exclamation marks are all about. At first glance it almost reads like the script of a five o’clock soap opera! See what we did there with the exclamation mark? This part of the Swift syntax is potentially the most confusing to a newcomer, so if you want it demystified, you have come to the right place.

What Is an Optional Type?

Let us start revealing what optionals are by looking at how Swift deals with “normal” types. Consider the example in Listing 19-11, which declares an integer and attempts to print it out.

Listing 19-11. Declaring a Variable of Non-optional Type
var age: Int
print("Person's age = (age)")


// This generates a compiler error:
// variable 'age' used before being initialized

These two lines generate a compiler error: error: variable 'age' used before being initialized. ActionScript defaults to automatically setting to null or to zero variables that you don’t explicitly initialize. Other languages are less merciful and will leave whatever junk is in the memory the variable in question happens to point at. If we had a penny for every hour we have spent in the past chasing a mysterious crash only to trace it down to an uninitialized variable. . . .4 This bitter experience makes us grateful that the Swift compiler can be of some help: catching situations like this at compile time is invaluable.

Next, let us see what happens if you try to initialize the age variable by assigning nil to it like we can do by assigning null in ActionScript (see Listing 19-12).

Listing 19-12. Initializing a Non-optional Type of Variable with Nil
var age: Int = nil
print("Person's age = (age)")
// error: nil cannot initialize specified type 'Int'

This change begets a different compiler error: error: nil cannot initialize specified type 'Int'. In Swift you can’t assign nil to just any variable. You need to explicitly tell the Swift compiler that this variable can take nil as a value. This could be useful if age was assigned the result of a third-party function, which may return nil, for example. Listing 19-13 shows you how to let the compiler know that the age variable can take nil.

Listing 19-13. Declaring a Variable of Optional Type and Initializing It with Nil
var age: Int? // the question mark tells the compiler that age can be nil
// This declaration also automatically initializes age with nil.

The question mark after the type signals to the Swift compiler that age can accept the nil value. In other words, having an actual integer value is optional: age can contain an integer or it can contain nil.

Note that the declaration above also initializes age with nil (i.e., it is shorthand for var age: Int? = nil). In other words, if you don’t provide a value for your optional variable, it is automatically set to nil.

Why Use Optionals?

Let us have another look at the age variable from the last two examples. Suppose you have a database query, which takes a person’s name and tells you how old they are (see Listing 19-14).

Listing 19-14. Assigning a Function Result to a Non-optional Variable
func getAgeFromName(name: String) -> Int {
    // query a database and return a result
}


var age: Int = getAgeFromName("John Smith")

An important question is: how do you deal with results for names that are not found in the database? Return 0? Return -1? Zero is less suitable of the two, as it is a valid age, but it may be your only choice if you were forced to use UInt, instead of Int. In either case you would need to add comments to document your intentions about which value should be interpreted as “no information about this person’s age.”

How about returning something that is neither Int nor UInt? Having getAgeFromName return nil to indicate missing information has two merits: it’s consistent and self-documented.5 Listing 19-15 shows this subtle change.

Listing 19-15. Assigning a Function Result to an Optional Variable
func getAgeFromName(name: String) -> Int? {
    // Query a database and return a result.
    // The result can now be nil.
}


let age: Int? = getAgeFromName("John Smith")
// If age contains any integer value, you know that “John Smith”
// was found in the database. A nil result on the other hand tells you
// that either the name was not found or age information was missing for it.

Note that in both listings you can omit the type from the declaration of age, as the compiler will infer it from the function result you assign to it:

let age = getAgeFromName("John Smith").

Unwrapping an Optional

Unwrapping is fancy speak for accessing the value of an optional variable or constant. For this to make complete sense it might be a good idea to peek behind the scenes first. In Swift Optional is actually a separate type, which looks like Listing 19-16.

Listing 19-16. The Optional Type in Swift
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
    case None
    case Some(Wrapped)


    // Initializers and methods follow...
}

Unless you skip to Chapter 22 , some of the syntax above, namely, enum and <Wrapped>, may look unfamiliar. For the moment you can think of the code in Listing 19-16 like this: it declares a type, named Optional, which can have two states: None, which means “no value” and Some, which means “some value of type Wrapped.” Here Wrapped is not an actual type but a type placeholder. You can declare an Optional variable that can hold an Int value: Optional<Int> or a String value: Optional<String> or whatever value type you decide to plug in place of Wrapped.

The ? suffix after an optional-type declaration is syntactic shortcut for Optional<YourTypeHere>, so the following two declarations in Listing 19-17 are the same.

Listing 19-17. Two Different Ways of Declaring an Optional Variable
// These two lines declare the same thing:
var age: Int?
var age: Optional<Int>
Tip

Optional<Wrapped> is a generic enumerated type. You can find information and examples on enumerations and generics in Chapter 22.

So when you declare an optional Int variable, this effectively means you are not declaring an Int that might be nil but an Optional that may or may not contain an Int value. Getting this value out of the Optional variable is called unwrapping.

You can access the value of an Optional in one of two ways:

  • by assigning its value to a temporary constant or variable, called optional binding; and

  • by using forced unwrapping.

You can also use a third way, which requires a slightly different kind of optional, called implicitly unwrapped optional. We will have a brief look at each of these choices in the next few subsections.

Optional Binding

You bind an optional’s value by assigning it to a temporary constant or a variable. This allows you to perform a check for whether a value is available or whether the optional contains nil.

Listing 19-18 shows an example that extracts the value of the optional age variable into the constant tempAge. The code following the if statement will only be run if a non-nil value is assigned to tempAge.

Listing 19-18. Optional Binding
var age: Int? = 64
if let tempAge = age {
    // Use tempAge
}

Forced Unwrapping

Force-unwrapping an optional is a bit dramatic. First, you need to be confident that what you unwrap has a non-nil value. Second, you use the exclamation mark suffix to do the unwrapping (see Listing 19-19). What could declare confidence better than that?

Listing 19-19. Forced Unwrapping of an Optional
var sureAge = age! // The exclamation mark unwraps age's value

This line effectively says “I am sure that there is a non-nil value inside this variable and am not afraid to use it.”. The consequence of overconfidence is a runtime error:

fatal error: unexpectedly found nil while unwrapping an Optional value.

Implicitly Unwrapped Optionals

Instead of using the exclamation mark to unwrap an optional every time you need its value, you can declare an optional variable in a way that makes sure its value is unwrapped when you access it. Such a variable is called implicitly unwrapped and you can create one by adding an exclamation mark after its type (see Listing 19-20).

Listing 19-20. Declaring an Implicitly Unwrapped Optional
var implicitlyUnwrappedAge: Int! = 64
Note

Here is some behind-the-scenes information. In Swift 2 implicitly unwrapped optionals have a type of their own: ImplicitlyUnwrappedOptional<Wrapped>. In other words, the declaration in Listing 19-20 is a shortcut for

var implicitlyUnwrappedAge: ImplicitlyUnwrappedOptional<Int> = 64                    

Swift 3 keeps the syntax for declaring implicitly unwrapped optionals but treats the exclamation mark as a type attribute. So, implicitlyUnwrappedAge from the last example would no longer be of type ImplicitlyUnwrappedOptional<Int>, but of type Optional<Int> with a value that may be implicitly forced.

As is the case with optionals, if you do not provide an initial value when you declare an implicitly unwrapped optional, it is automatically initialized with nil. You can think of an implicitly unwrapped optional as a hybrid between an optional and a non-optional variable or constant: it has the option of containing nil, but can also be used as your usual variable or constant without the need to unwrap its value every time (Listing 19-21).

Listing 19-21. Accessing the Value of an Implicitly Unwrapped Optional
let sureAge = implicitlyUnwrappedAge // no need for an exclamation mark

Using implicitly unwrapped optionals has its pros and cons. On the one hand, it unclutters your code from exclamation marks. On the other hand, a variable which contains nil when it shouldn’t makes your code more likely to break and, at the same time, makes it less obvious why it broke. For instance, if we did not initialize implicitlyUnwrappedAge and then tried to print it out, this would result in a runtime error (Listing 19-22).

Listing 19-22. Accessing an Implicitly Unwrapped Optional, Which Has Been Initialized with Nil
var implicitlyUnwrappedAge: Int! // The variable is is initialized with nil.
print(implicitlyUnwrappedAge) // This results in a runtime error.

Optional Chaining

Optional chaining saves you the need to check if an optional variable or constant is nil before you access its properties or call one of its methods. What you get as a result of that access, instead of a slap on the wrist, is another optional value.

Let us illustrate this with an example. The code in Listing 19-23 declares two classes: Person and PersonalInformation. PersonalInformation contains a single property: age of type Int. Person contains a single property too, personalInfo, of type PersonalInformation?, which may optionally contain an instance of PersonalInformation or may contain nil.

Listing 19-23. Declaring a Class with an Optional Property
class PersonalInformation {
    var age = 20
}


class Person {
    var personalInfo: PersonalInformation?
}

In Listing 19-24 the first line instantiates cookieMonster of type Person. Because no initial value is given to its property personalInfo, it is automatically assigned nil. So what’s going on on the second line, where cookieMonster.personalInfo? is accessed and why doesn’t it result in an exception?

Listing 19-24. Accessing a Property Through Optional Chaining
var cookieMonster = Person()
var monstersAge = cookieMonster.personalInfo?.age
print(monstersAge)


// output: nil

Accessing a property (or calling a method or subscript) on an optional variable that may be nil and getting away with it is what optional chaining affords you. The way the compiler does it is: if cookieMonster.personalInfo? is nil, then the result of cookieMonster.personalInfo?.age is inferred to be an optional, which contains nil. The compiler then infers the same type for monstersAge, which gets assigned that result.

What’s Your Type?

When coding, you can have situations where you either need to check if an object is of a certain type or to find out what type it is. In this section we see how you can do these checks.

Using the is Operator

You check if an object is of a certain type with the is operator (see Listing 19-25). It takes an instance and a type and returns true if the instance is of that type or descends from it, false otherwise.

Listing 19-25. Using the is Operator for Type Checking
var randomInt = 42
var amIString = randomInt is String // returns false
var amIInt = randomInt is Int // returns true

Type Queries

You can query an instance’s runtime type with obj.dynamicType, as in Listing 19-26.

Listing 19-26. Querying an Object’s Type
var instanceOfInt = 66
print("(instanceOfInt.dynamicType)") // prints Int
Note

In Swift 3 dynamicType is no longer a property of the type but an operator. In other words, instead of calling obj.dynamicType, you will need to call dynamicType(obj).

You can obtain the type of a class or a protocol as a value with the postfix expression self. Listing 19-27 shows you a couple of examples.

Listing 19-27. Querying the Type of a Class and a Protocol
print("(MyClass.self)") // prints MyClass
print("(MyProtocol.self)") // prints MyProtocol

Type Casting

Like ActionScript, Swift offers you the as operator for type casting . Unlike the ActionScript version, it comes in two flavors: as? and as! If you have read the previous section on optionals, this syntax will look familiar.

The as? version of the operator returns an optional value, which is set to nil if the cast is unsuccessful. The as!, otherwise known as the forced form of the operator, does not give you the option of checking but throws a runtime error when the type casting is not possible. Use it when you feel confident in your type casting . Or not at all.

The examples that follow illustrate the two scenarios. Listing 19-28 declares an Int instance, tries to cast it as a String, and, if the cast succeeds, assigns the result to a temporary constant, named stringVersion. Note how the declaration of stringVersion uses optional binding, which we covered earlier in the section “Optionals.”

Listing 19-28. Using the as? Operator for Type Casting
var instanceOfInt = 42

if let stringVersion = instanceOfInt as? String {
    print("Casting an Int to a String succeeded: (stringVersion)")
}
else {
    print("The cast did not succeed.")
}


// output: "The cast did not succeed."

Using the same integer instance, instanceOfInt, Listing 19-29 casts it to a String and force-unwraps the result, which leads to a runtime error.

Listing 19-29. Using the as! Operator for Type Casting
print("Casting an Int to a String: (instanceOfInt as! String)")
// throws a runtime error

If you try these examples at home, you will notice that the compiler very gracefully issues a warning at the line of type casting, telling you that Cast from 'Int' to unrelated type 'String' always fails.

Nested Types

In Swift a type can be defined within the context of another type. The example in Listing 19-30 defines the class HostClass, which contains two levels of hosted classes: FirstNestedClass and SecondNestedClass. This demonstrates the syntax for nested types: the definition of the nested type must be put within the curly brackets of its host. It also shows you that you can use more than one level of nesting.

Listing 19-30. Defining Nested Types
class HostClass {
    class FirstNestedClass {
        class SecondNestedClass {
            let secondNestedClassField = "I am three levels deep."
        }
    }
}

The purpose of nesting types within one another usually is to keep a type within the context in which it is needed: for example, if HostClass is the only class that uses FirstNestedClass, it would make sense to keep FirstNestedClass’s definition within the scope of HostClass. However, if you need to access a nested type outside its host, you can do that by prefixing its name with the name of its enclosing type(s) as shown in Listing 19-31.

Listing 19-31. Accessing a Nested Type Outside Its Containing Type
let secondNestedInstance = HostClass.FirstNestedClass.SecondNestedClass()
print("(secondNestedInstance.secondNestedClassField)")

Some Types Worth Getting to Know

We will now have a look at a few types you are likely to use quite a lot when you program with Swift: containers, tuples, and function types.

Container Types: Array, Set, Dictionary

The container types in Swift are implemented as generic classes. We will go over generics in Chapter 22 in detail. For now, you can think of a generic class like this: Array<String> stands for “an array of String items and nothing else” and Array<Int> stands for “an array of Int items and nothing else.” By “nothing else” here we mean that an Array, or a Set in Swift contain items of the same type: you can have an array of Strings or an array of Ints, but you can’t have an array that contains a String and an Int.

Array

Let us see how we can manipulate arrays in Swift: from creating and initializing them to iterating through their items and making modifications.

Ways of Creating and Initializing an Array

Following are some typical ways you may use to create an Array and give it initial values. The first example (Listing 19-32) shows you how to create an empty array of String items. The square brackets tell the Swift compiler that you are declaring an array and the type between them is the type of the items it will contain.

Listing 19-32. Creating an Empty Array
var theThreeMusketeers = [String]()

Note that this is shorthand for writing var theThreeMusketeers : Array<String> = [].

Tip

If Array<String> looks strange, have a look at the section on generics in Chapter 22.

Listing 19-33 shows you how to declare an array with an initial size and an initial value for its items.

Listing 19-33. Creating Array of a Given Size and Initializing Its Members with a Certain Value
var theThreeMusketeers = [String](count: 3, repeatedValue: "")

You can also create an array using an array literal: the syntax in the Listing 19-34 will look familiar from ActionScript. Note how once you provide literal values the compiler is able to infer their type, so you can be concise in your declaration and omit the type declaration part.

Listing 19-34. Initializing an Array with an Array Literal
var theThreeMusketeers = ["Athos", "Porthos", "Aramis"]
var extraMusketeers = ["D'Artagnan"]

Finally, you can combine the arrays you declared previously and create a third one, as in Listing 19-35, using the + operator.

Listing 19-35. Creating an Array by Combining Two Other Arrays
var theFourMusketeers = theThreeMusketeers + extraMusketeers
How to Iterate over an Array

You can use a for-in loop to iterate over the items in an array. For example, see Listing 19-36.

Listing 19-36. Iterating over an Array Using a for-in Loop
for musketeer in theFourMusketeers {
    print("Musketeer's name: (musketeer)")
}

Here is a neat trick, shown in Listing 19-37: the Array class has a method, called enumerate, which allows you to iterate not only over the items in the array but also over their indices. Neat, huh?

Listing 19-37. Iterating over an Array by Calling Its Enumerate Method
for (index, value) in theFourMusketeers.enumerate() {
    print("(index + 1). Musketeer's name: (value)")
}
Tip

We will go over the specifics of the for-in loops in Swift in Chapter 20.

Working with the Items of an Array

You can query the number of items in an array by calling its count method. The isEmpty method lets you check whether the array has any items in it. The next couple of examples in Listing 19-38 and Listing 19-39 show you how to add items to an array and how to modify them. Some of the ways will look familiar to you from ActionScript. Others may be new (e.g., replacing a range of array items with a new array), as shown on the third line of code of Listing 19-39.

Listing 19-38. Adding Items at the End of an Array
var theThreeMusketeers = [String]()

// adds "Athos" to the end of the array:
theThreeMusketeers.append("Athos")
// the array contains [“Athos”]


// concatenates theThreeMusketeers with the array ["Porthos", "Aramis"]
theThreeMusketeers += ["Porthos", "Aramis"]
// the array now contains ["Athos", "Porthos", "Aramis"]
Listing 19-39. Modifying the Values of Array Items
// creates an array of four Strings and initializes them with an empty string:
var theFourMusketeers = [String](count: 4, repeatedValue: "")
// the array contains ["", "", "", ""]


// replaces the first string in the array with "D'Artagnan":
theFourMusketeers[0] = "D'Artagnan"
// the array now contains ["D'Artagnan", "", "", ""]


// replaces the range of items 1, 2 and 3 with new values:
theFourMusketeers[1...3] = ["Athos", "Porthos", "Aramis"]
// the array now contains ["D'Artagnan”, "Athos", "Porthos", "Aramis"]

Inserting and removing an item at a specific index is done with the insert(_:atIndex:) and the removeAtIndex(_:) methods—very similar to the insertAt and removeAt methods of the ActionScript Array.

Set

A Setin Swift represents a collection of unique items. You can think of it as an Array, which can only contain the same value once. Like an Array, a Set collection is unordered. All of the functionality for working with arrays applies to sets too, except that there is no shorthand form for declaring a Set (Listing 19-40).

Listing 19-40. Creating a Set and Initializing It with an Array Literal
var fireflyCharacters: Set<String> = ["Mal", "Inara"]

You can perform logical set operations, using the union(_:), subtract(_:), intersect(_:), and exclusiveOr(_:) methods of the Set class. All of these create a new set and return it as a result. For example, the code in Listing 19-41 finds the intersection between two sets.

Listing 19-41. Finding the Intersection Between Two Sets
var fireflyCharacters: Set<String> = ["Mal", "Inara"]
var serenityCharacters: Set<String> = ["Mal", "Inara", "Kaylee", "Simon"]


var commonCharacters = fireflyCharacters.intersect(serenityCharacters)
// commonCharacters contains ["Mal", "Inara"]

Swift gives you the ability to compare sets and determine if they are equal by using the equals operator (==). You can also check if one set contains another set by using the isSubsetOf(_:)/isStrictSubsetOf(_:) and isSupersetOf(_:)/isStrictSupersetOf(_:) methods. The isDisjointWith(_:) method returns true if two sets have no items in common.

Dictionary

If you use dictionaries in ActionScript, you won’t have much of a learning curve with the Swift Dictionary type. Like its ActionScript cousin, it stores a collection of associated key-value pairs. The next few subsections show ways of creating and initializing a Swift Dictionary.

Ways of Creating and Initializing a Dictionary

The example in Listing 19-42 creates an empty dictionary, in this case with keys of type String and values of type Double.

Listing 19-42. Creating an Empty Dictionary
// creates an empty dictionary with keys of type String and Double values:
var macOsXVersions = [String: Double]()

The line in Listing 19-42 creates an empty dictionary with keys of type Double and values of type String. Note that this is the shorthand notation for

var macOsXVersions : Dictionary<String, Double> = [:].

Tip

For a detailed explanation of the angle brackets syntax go to the section on generics in Chapter 22.

Listing 19-43 shows you how to create a dictionary and initialize it with a dictionary literal. You can see the Swift type inference in action here: you don’t need to explicitly specify the key-value types: the compiler guesses them from the literals you provide.

Listing 19-43. Initializing a Dictionary with a Dictionary Literal
// creates a dictionary with keys of type String and values of type Double:
var macOsXVersions = ["Cheetah" : 10.0, "Puma" : 10.1, "Jaguar" : 10.2]
Iterating over a Dictionary

The for-in loop is your friend here too. With dictionaries you have an option to iterate over keys and values together (Listing 19-44) or separately (Listing 19-45).

Listing 19-44. Iterating over a Dictionary’s Keys and Values
for (versionName, versionCode) in macOsXVersions {
    print("(versionName) (versionCode)")
}
Listing 19-45. Iterating over a Dictionary’s Keys and Values Separately
// Iterating through the keys only
for versionName in macOsXVersions.keys {
    print("Mac OS X version name: (versionName)")
}


// Iterating through the values only
for versionCode in macOsXVersions.values {
    print("Mac OS X version code: (versionCode)")
}
Accessing and Modifying Keys and Values

The next few examples in Listing 19-46, Listing 19-47, and Listing 19-48 show you how to add, modify, and remove items from a dictionary.

Listing 19-46. Adding a New Key-Value Pair to a Dictionary
macOsXVersions["Panther"] = 0.0
// macOsXVersions now contains
// ["Cheetah" : 10.0, "Puma" : 10.1, "Jaguar" : 10.2, "Panther" : 0.0]

When you need to modify an existing value for a given key, you can do that by accesing the value directly, using a subscript, represented by the square brackets surrounding the key (more on subscripts in Chapter 22 ). You can also call the updateValue method of the Dictionary class, which gives you a chance to retain the old value of the key after you have replaced it with a new one (Listing 19-47).

Listing 19-47. Modifying the Value for a Given Key
// You can update a value using subscript
macOsXVersions["Panther"] = 0.3
// macOsXVersions now contains
// ["Cheetah" : 10.0, "Puma" : 10.1, "Jaguar" : 10.2, "Panther" : 0.3]


// ... or you can call the updateValue(_:forKey:) method
if let oldValue = macOsXVersions.updateValue(10.3, forKey: "Panther") {
    print("The old value for Mac OS X 10.3 was (oldValue).")
}
// macOsXVersions now contains
// ["Cheetah" : 10.0, "Puma" : 10.1, "Jaguar" : 10.2, "Panther" : 10.3]

To remove a key-value pair from a Dictionary you simply replace the value with nil, as shown in Listing 19-48.

Listing 19-48. Removing a Key-Value Pair from a Dictionary
macOsXVersions[“Panther”] = nil
// macOsXVersions now contains
// ["Cheetah" : 10.0, "Puma" : 10.1, "Jaguar" : 10.2]

Tuples

Tuples are useful for temporarily keeping a group of values together. “Temporarily” here means as a way of returning multiple values from a function, for example. To keep values together that will have a longer lifespan than that, you would normally be better off keeping them in a class or a structure.

You define a tuple as a comma-separated list of types, enclosed in parentheses. Listing 19-49 declares a tuple, weatherStats, which contains two values: wind speed in miles per hour and wind direction. This means that the tuple’s type is (Int, String).

Listing 19-49. Defining a Tuple
let weatherStats = (7, "North") // Tuple of type (Int, String)

You can read the values in a tuple by assigning them to a temporary constant. The next few examples show you how to do that and how to read or ignore values selectively.

The code in Listing 19-50 assigns the values of the tuple we just declared to two temporary constants: windSpeed and windDirection.

Listing 19-50. Accessing Individual Values in a Tuple
let (windSpeedMph, windDirection) = weatherStats
print("wind speed: (windSpeedMph) mph, direction: (windDirection)")

You don’t have to define constants for all of the tuple’s values if you are not interested in all of them. Use underscore to ignore values that you don’t need to access (Listing 19-51).

Listing 19-51. Selectively Accessing Individual Values in a Tuple
let (_, windDirection) = weatherStats
print("wind direction: (windDirection)")

Each of the tuple’s values has an index which you can access by putting a dot, followed by the index’s number after the name of the tuple. Calling weatherStats.0 gives you access to the first member of the tuple, weatherStats.1—to the second, and so on, as shown in Listing 19-52.

Listing 19-52. Using an Index to Access a Tuple’s Member
print("wind speed: (weatherStats.0) mph, direction: (weatherStats.1)")

As an alternative to using indices, you can name each tuple member and use its name to access it. Listing 19-53 defines the tuple weatherStats with two named elements—windSpeed and windDirection—and then accesses their values as weatherStats.windSpeed and weatherStats.windDirection.

Note

To use names, a tuple must have at least two elements.

Listing 19-53. Using Names for Tuple Elements
let weatherStats = (windSpeed: 7, windDirection: "North")
print("wind speed: (weatherStats.windSpeed) mph,
direction: (weatherStats.windDirection)")

The example in Listing 19-54 shows you a function, which returns a tuple.

Listing 19-54. A Function, Which Returns a Tuple as a Result
func getWeatherStats() -> (Int, String) {
    return (7, "North")
}

The code in Listing 19-55 shows the ActionScript analogue of the tuple we defined in Listing 19-53.

Listing 19-55. Using Dynamic Objects in ActionScript Is Close in Syntax to Swift’s Tuples
// ActionScript code:
var weatherStats : Object = {windSpeed: 7, windDirection: "North"};
trace("wind speed: " + weatherStats.windSpeed + "mph,
direction: " + weatherStats.windDirection);

Function Types

In Swift the type of a function is its signature. Listing 19-56, for example, declares a function of type (String) -> String. This means that the function takes one argument of type String and returns a String result.

Listing 19-56. Declaring a Function of Type (String) -> String
func capitalize(str: String) -> String {
    return str.uppercaseString
}

When you know a function’s type, you can assign it to a variable, as shown in Listing 19-57. You define the type of the variable as ( functionArguments ) -> functionReturnType .

Listing 19-57. Assigning a Function to a Variable
// the next line declares a variable
// and assigns a function of type (String)->String to it:
let capitalizeFunction: (String) -> String = capitalize


// accessing the variable and giving it an argument
// results in the function being called:
print("capitalization: (capitalizeFunction("fallaciloquence"))")

You can also pass a function type as an argument to another function. Again, you use the function’s signature as the type of the argument, so capitalizationFunction’s type in Listing 19-58 is (String) -> String.

Listing 19-58. Using a Function Type as a Parameter to Another Function
// This function takes another function as an argument:
func printCapitalizedString(capitalizationFunction: (String) -> String,
                                str: String) {
    let capitalizeFunction: (String) -> String = capitalize
    print("capitalization: (capitalizeFunction("fallaciloquence"))")
}

Or, you can have a function that returns another function as a result. A bit weird, but only syntax-wise. The first arrow in Listing 19-59 is put before the return type of getCapitalizationFunction. The second arrow is part of its return type: (String) -> String.

Listing 19-59. Returning a Function Type as a Result of Another Function
func getCapitalizationFunction() -> (String) -> String {
    return capitalizeFunction
}

Type Aliases

Finally, you can name any existing type on a whim using the typealias keyword. Say you are writing an app that takes payments and want a more meaningful name for the type you would use to store prices. Using Price as an alias to UInt makes it obvious not only that it allows only non-negative values but also why (Listing 19-60).

Listing 19-60. Defining a Type Alias
public typealias Price = UInt

Summary

You are now familiar with the basics of the types in Swift and know how to take advantage of the compiler’s type inference, how to query an instance’s type, and how to type cast it. This chapter has also, it is hoped, demystified Swift’s syntax weirdness around optional types and now you are comfortable with all of the question marks and exclamation marks in the code. Add to that some essential information on a bunch of important types and you are set to go!

Footnotes

1 To quote Martin Fowler, author of books on refactoring and UML (Unified Modeling Language) among other valuable topics: “Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”

2 Not 100% true, actually. . . . In ActionScript strings are made to behave as if they are passed by value and copies of them are created only if you modify the String instance via one of its references.

3 Swift does copy-on-write for value types, so actual copying will happen only if you modify the instance you have assigned or passed as an argument to a function (inout function arguments are an exception—see Chapter 21 ).

4 A crime always committed by another colleague, never by us. Honest!

5 Plus, it requires that the caller unwrap the result, so your colleagues can’t claim they didn’t know nil was a possibility. We will see what unwrapping is in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset