© 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_22

22. New and Different Concepts

Radoslava Leseva Adams and Hristo Lesev2

(1)London, UK

(2)Kazanlak, Bulgaria

In this chapter we open the door to four topics that either have no counterpart in ActionScript or that Swift redefines in a way that can make them seem new to an ActionScript developer. Enumerations and generics fall under the first category. Subscripts and closures, although present in ActionScript, are taken to a whole new level in terms of functionality and expressiveness. Curious? I was . . .

Enumerations

An enumeration or enum is a type that you declare to define a set of names, which can also have values attached to them.

Why Use Enumerations?

Let us consider the alternative. Suppose one part of your app sends images to a server and generates a code that tells you whether the upload succeeded. You may want to write a function that processes this code and returns something more meaningful that you can either show on the screen or add to a log file. Let us say that you have three codes to consider:

  • 0 means that the upload was successful;

  • 1 means that the upload failed because of a server error;

  • 2 means that the upload failed because the user canceled it.

Listing 22-1 shows what your processing function might look like.

Listing 22-1. Making a Choice Without Using Enumerations
func getMessageFromCode(resultCode : Int) -> String {
    switch errCode {
    case 0: // Success
        return "The file was sent successfully."


    case 1: // Server error
        return "The server responded with an error."


    case 2: // The user cancelled sending
        return "The user aborted the operation."


    default:
        return "Unknown error code"
    }
}

This function does the job, but there are a couple of things that can be improved upon.

  • You need to keep track of an arbitrary mapping between result codes and their meaning. If you add new codes to the part of your app that deals with the uploads, you must remember to add them to geMessageFromCode too.

  • The function doesn’t give you the flexibility for additional information: for example, finding out the reason for the server failure based on an error code that might have come from the server.

Using an enum with Raw Values to Handle Value Mapping

Using named constants instead of 0, 1, and 2 would partially solve the problem with the arbitrary mapping between return codes and their meaning. Using an enumeration can give you that and more: it lets you define a set of names and (optionally) attach constant values to them. It also keeps the set’s integrity: unlike with named constants, in an enumeration you don’t have to worry about two constants having the same value by accident.

To declare an enumeration you use the keyword enum, the name of the enumeration and list the set of names in curly brackets (see Listing 22-2).

Listing 22-2. Declaring an enum of Integers
enum FileUploadResult : Int {
    case Success = 0
    case ServerError = 1
    case UserCancelled = 2
}

Having declared this, you can use FileUploadResult.Success (or simply .Success) where you need 0, .ServerError where you need 1, and so on. Let us rewrite getMessageFromCode to take advantage of this, as shown in Listing 22-3.

Listing 22-3. Replacing Hard-Coded Integers with Names from an enum
func getMessageFromCode(resultCode: FileUploadResult) -> String {
    switch errCode {
    case .Success:          return "The file was sent successfully."
    case .ServerError:      return "The server responded with an error."
    case .UserCancelled:    return "The user aborted operation."
    }
}

There are a few things that are worth noting in this improved version of getMessageFromCode:

  • The integers we assigned to the enum members are called raw values in Swift.

  • The code is now self-documented and does not need comments.

  • FileUploadResult is a new type, which defines a closed set of integers. As a result the resultCode argument can now make use of this type and have its possible values limited to the only three that have meaning in this context.

  • You don’t need a default statement any more. As you saw in Chapter 20 , the Swift compiler insists that a switch statement be exhaustive: it either needs to provide a case statement for every possible value of the type it handles or have a default clause to cover values that do not have case statements. The case statements here cover every possible value of the FileUploadResult type.

  • A side effect of omitting the default clause is that you don’t have to remember to add new case statements if you create new return codes: the compiler will let you know if you fall short on case statements.

  • The .Success, .ServerError, etc. syntax is short for FileUploadResult.Success, FileUploadResult.ServerError, and so on.

More Facts About Raw Values

Here are a few more things that are good to know about raw values in enumerations:

  • Raw values are optional. You can define an enum of names without values attached to them. The code in Listing 22-4 shows you an enum without raw values and another valid syntax, where you list names on the same line:

    Listing 22-4. Enum Without Raw Values
    enum FileUploadResult {
        case Success, ServerError, UserCancelled
    }
  • Integer raw valuescan auto increment. If you define an enum with integer raw values but only assign a value to one of its members, all members that come after it will assume raw values that autoincrement from the assigned one. For example, in Listing 22-5 the declaration of FileUploadResult .ServerError automatically assumes a raw value of 1 and .UserCancelled a raw value of 2.

    Listing 22-5. Autoincremented Raw Values
    enum FileUploadResult: Int {
        case Success = 0, ServerError, UserCancelled
    }
  • Raw values can be strings, characters, and integer or floating-point numbers.

  • Raw values are constant and have the same type for all members of an enum. Once assigned, raw values cannot be changed and are a characteristic of the enum type you have declared: each instance of the enum will have the same raw values assigned to its members. When you want to use raw values, you need to tell the compiler what type they will be by declaring your enum to be of that type (an Int or a String, for example). Each member of the enum is then considered to be of the same type, whether it has a raw value assigned to it or not.

  • To read a raw value, use the rawValue property:

    Listing 22-6. Accessing a Raw Value
    let returnCode = FileUploadResult.Success.rawValue

Adding Associated Values to Handle Variable Information

When it comes to enumerations most programming languages stop at what you saw in the last example: an enum allows you to use named integers. And this is usually good enough.

Swift, on the other hand, offers more. Instead of a raw value, a member of an enum type can have another kind of value mapped to it, called associated value. You can think of an associated value as an additional property of the enum member. It can be of any type, it can store custom information, and you can change the value of that information from one line of code to the next.

In the file upload result example the enum passed to getMessageFromCodecould have more detailed information attached to its members. For example, FileUploadResult.ServerError could have an Int and a String that indicate an error code and a message from the server; FileUploadResult.UserCancelled could have an additional String that explains how the user canceled the upload. In Swift you express that by providing a list of associated value types in brackets after an enum member’s name (see Listing 22-7).

Listing 22-7. Syntax for Associated Values in enums
enum FileUploadResult {
    case Success                   // doesn’t need an associated value
    case ServerError (Int, String) // (server error code, server message)
    case UserCancelled (String)    // (how the user cancelled the upload)
}

Note how this declaration specifies only types associated with two of the members without assigning values to them. This is like declaring variables that can be attached to .ServerError and .UserCancelled that you populate once you create an instance of FileUploadResult, as shown in Listing 22-8.

Listing 22-8. Assigning Associated Values
var returnCode = FileUploadResult.ServerError(400, "Bad request")

// You can later change the associated values in returnCode
// by doing another assignment:
returnCode = FileUploadResult.ServerError(404, "Not found")

Listing 22-9 shows a version of getMessageFromCode, which makes use of the enum’s associated values.

Listing 22-9. Using associated values
func getMessageFromCode(resultCode : FileUploadResult) -> String {
    switch resultCode {
    case .Success:
        return "The file was sent successfully."


    case .ServerError (let serverErrorCode, let serverMessage):
        return "The server responded with error:
                (serverErrorCode), (serverMessage)."


    case .UserCancelled (let additionalInfo):
        return "The user aborted operation.
                Additional information: (additionalInfo)"
    }
}

Well, this looks like a handful. For the sake of simplicity let us dissect just one of those case statements (see Listing 22-10).

Listing 22-10. Reading Associated Values
case .UserCancelled (let additionalInfo):
        return "The user aborted operation.
                Additional information: (additionalInfo)"

The code in the brackets inside the case statement declares a local constant called additionalInfo and assigns the associated value of .UserCancelled to it. The next line then uses that variable to build a string and return it as the function’s result. This solves the second issue we had with the very first version of getMessageFromCode: its argument can now carry additional information.

It is worth noting that the values you read in the code in Listing 22-10 are part of the resultCode instance of FileUploadResult, not part of the FileUploadResult type or .UserCancelled. In other words, if you declare another FileUploadResult instance and add associated values to it, they can be different from those in resultCode.

More Facts About Enumerations

Some of the rules that apply to enumerations might surprise you, for example:

  • Raw and associated values are mutually exclusive. Currently the Swift compiler will not let you declare an enum that has both raw and associated values attached to its members.

  • An enum is like a class. It can be extended, can conform to protocols, and can have instance methods and initializers defined for it.

Subscripts

Subscripts do have a counterpart in ActionScript. Swift, however, puts a new spin on this concept, which we will focus on here.

Defining a Subscript

In Swift you can use subscripts in similar situations and, unlike in ActionScript, you can also define them. In other words, you can decide what happens when dictionary[3] is called.

I will illustrate this with an example. In Swift you can access the individual characters in a String by providing an instance of a class called Index, but you can’t simply ask for str[3]. Let us extend the String class and define a subscript that takes integers to make this possible.

You declare a subscript like you would a method, but use the subscript keyword, instead of func, as in Listing 22-11.

Listing 22-11. Defining a Subscript in Swift
extension String
{
    public subscript (i: Int) -> Character
    {
        let index = self.startIndex.advancedBy(i)
        return self[index]
    }
}

Now you can access the characters of a given String with an integer literal in square brackets (see Listing 22-12).

Listing 22-12. Using a Subscript
let str: String = "I swear by my pretty floral bonnet..."
let ch: Character = str[3] // returns 'w'

Subscript Overloading

You were acquainted with the concept of overloading operators in Chapter 18 and saw how to overload methods in Chapter 21 . Subscripts, being just another type of function, can be overloaded too. The only condition is that the subscript methods defined for a given type all have different signatures.

In fact, what we provided in the last example was a subscript overload to the already existing String subscript that takes an Index instance and returns a character.

Subscript Varieties

In Swift you can define subscripts that take ranges, multiple dimensions, or indices that are not numbers.

Using a Range

The example in Listing 22-13 defines another subscript for the String type, which takes a range, a lower index and an upper index, and returns the substring of characters that fall between (and including) these indices.

Listing 22-13. Defining a Subscript That Uses a Range
extension String {
    subscript(characterRange: Range<Int>) -> String {
        let start = self.startIndex.advancedBy(characterRange.startIndex)
        let end = self.startIndex.advancedBy(characterRange.endIndex)
        let range = start..<end
        return self[range]
    }
}


// You can now get a subset of the string's characters like this:
let str: String = "I swear by my pretty floral bonnet..."
let chSubset = str[3…6] // returns 'wear'

Using Multiple Dimensions

Subscripts can be multidimensional too. The code in Listing 22-14 defines a structure that represents a Tic-tac-toe board and can tell you what’s in a given cell of the board, defined by a row and a column.

Listing 22-14. Defining and using a multidimensional subscript
struct TicTacToeBoard {
    // Create the tic-tac-toe grid and initialize it with spaces:
    var mGrid: [[Character]] =
        [[" ", " ", " "], [" ", " ", " "], [" ", " ", " "]]


    func areIndicesValid(row: Int, column: Int) -> Bool {
        return 0…2 ∼= row && 0…2 ∼= column
    }


    subscript (row: Int, column: Int) -> Character {
        // Ensure that we are operating with valid indices:
        assert(areIndicesValid(row, column: column))
        return mGrid[row][column]
    }
}


// Call the two-dimensional subscript like this:
var ticTacToe: TicTacToeBoard = TicTacToeBoard()
let centreCell = ticTacToe[1, 1] // Returns " "

Using Non-numeric Indices

The indices you use in a subscript don’t have to be integers. For example, the two lines of code in Listing 22-15 initialize a Dictionary of String-Int key-value pairs and then use a String as the subscript index to access a value in the Dictionary:

Listing 22-15. Using String as the Subscript Index
let starWarsFilms =
        [ "Star Wars": 1977, "The Empire Strikes Back": 1980,
         "Return of the Jedi": 1983 ]
let firstStarWarsFilmRelease = starWarsFilms[ "Star Wars" ]! // returns 1977

Closures

Closures are not an entirely new concept for an ActionScript 3 developer. As with a lot of old tricks, however, you will find that Swift has reinvented this one too to offer more expressiveness. And this is what merits including closures in this chapter.

Closures in Swift Can Be Very Concise

Very. So concise indeed that some of the closure syntax can be baffling at first. We will ease into it with an example, which will first show you a familiar ActionScript closure and then its Swift counterparts, each of which will optimize some part of the syntax.

An ActionScript Example

Let us start with something familiar. In ActionScript 3 you can filter an array by calling the Array class’s filter function, which is defined thus:

function filter(callback: Function, thisObject:* = null): Array

Here the callback parameter is a closure: a function that you supply and that will be run on each element of the array. The manual page for filter tells us that callback is expected to have the following signature:

function filter(callback: Function, thisObject:* = null): Array

If you want to filter an array of integers and leave only the ones that are larger than three, this is what you might do. First, implement a filter callback function, then use it as an argument in the Array’s filter function (see Listing 22-16).

Listing 22-16. Closure Implementation and Use in ActionScript
// ActionScript code
// First, implement a callback function for filtering array elements:
function filterCallback(item : Object, index : int, array : Array) : Boolean {
    return item > 3;
}


// Then use the callback to filter an array:
var initialArray : Array = [1, 2, 5];
var filteredArray: Array = initialArray.filter( filterCallback );

After this is run, filteredArray should contain just [5].

Literal Translation to Swift

The Array container type in Swift happens to have the same method, called filter. How convenient. It takes a closure, which applies criteria to an element of the array and returns true if the element passes the criteria and false otherwise (Listing 22-17).

Listing 22-17. Defining a Closure and Using It in Swift
// Implement the closure function, which is run on each array element:
func includeElement(item : Int) -> Bool {
    return item > 3
}


// Then use the closure to filter an array:
let initialArray = [1, 2, 5]
let filteredArray = initialArray.filter(includeElement)

Optimization 1: Inlining a Closure Expression

Instead of defining a closure as a separate function, you can inline it where it is needed, as shown in Listing 22-18.

Listing 22-18. An Inline a Closure in Swift
let filteredArray = initialArray.filter(
    {(item: Int) -> Bool in return item > 3})

Note the following:

  • The closure is now anonymous (i.e., doesn’t have a name). The Swift SDK calls ‘unnamed closures written in a lightweight syntax that can capture values from their surrounding context’ closure expressions.

  • The full syntax for a closure expression looks like this: the whole expression is put in curly brackets and starts with the closure parameters in parentheses, followed by the closure return type, the keyword in, and the closure body:

{ (parameters) -> return type in closure body }

It is also worth noting that with this optimization we haven’t waded in deep Swift waters yet, as ActionScript allows you to define inline closures too.

Optimization 2: Letting the Compiler Infer Types from Context

The closure expression in Optimization 1 can be made even shorter by omitting the types of the argument and the result, as the Swift compiler will infer them from the context (Listing 22-19).

Listing 22-19. Closure Expression with Argument and Return Type Inferred
let filteredArray = initialArray.filter({item in return item > 3})

Optimization 3: Using an Implicit Return

If you look at the body of our closure expression: item > 3, you will notice a couple of things about it:

  • It’s a single expression.

  • Its result matches the return type of the closure, Bool.

The Swift compiler is quite happy for you to omit the return statement in cases of single-expression closures, which shortens code even further, as you can see in Listing 22-20.1

Listing 22-20. Using Implicit Return in Single-Closure Expressions
let filteredArray = initialArray.filter({item in item > 3})

Optimization 4: Using Shorthand Names for Parameters

Arguments passed to a closure can be referred to with what is called shorthand names in Swift. A shorthand nameis a symbol that represents an argument, based on where in the list of arguments it is. The convention is: $0 refers to the first argument in the list, $1 refers to the second argument in the list, and so on.

If you apply this convention the inline closure will look like Listing 22-21.

Listing 22-21. Using Shorthand Names in Closures
let filteredArray = initialArray.filter({$0 > 3})

Note that this also spares you the need to provide the list of arguments before the body of the closure and gets rid of the in keyword.

Optimization 5: Using an Operator Function

Think {$0 > 3} is short? How about getting rid of the curly brackets altogether and cutting this down to a single symbol?

To illustrate how this works, let us take a different example. Calling an Array’s sort function in Swift takes a closure with two arguments. The two arguments are elements of the array and the closure returns true if the first argument should be placed closer to the top of the array and false if the second argument should be placed closer to the top. Listing 22-22 is the long inline version of that closure, which helps sort the array in ascending order.

Listing 22-22. Using a Closure to Sort an Array
let sortedArray = initialArray.sort(
    { (item1: Int, item2: Int) -> Bool in return item1 < item2 } )

If you apply Optimization 4 to this, the closure will simply read {$0 < $1}. Turns out that in this case you can again rely on the ingenuity of the compiler to infer what you are trying to do and simply leave the less than operator to stand for the whole closure (see Listing 22-23).

Listing 22-23. The Sorting Closure Reduced to a Single Operator
let sortedArray = initialArray.sort(<)

Neat? Weird? Definitely.

More Facts About Closures

There are a couple more things to mention about closures. The first one provides an addition to our “strange syntax” collection:

  • Trailing closures. When you don’t have the luxury of a single-expression closure and instead have more work to do in the closure body, you can use what Swift calls a trailing closure. The only condition: the closure needs to be the last argument that is passed to a function. This is a call to the filter function we have been playing with, this time with a trailing closure. Note in Listing 22-24 how the closure comes in curly brackets after the argument list of the function. The parentheses after filter are optional if the closure is the only argument that you need to pass.

Listing 22-24. Using a Trailing Closure
let filteredArray = initialArray.filter() {
    $0 > 3
}


//You can even omit the parentheses after the function name in this case:
filteredArray = initialArray.filter{$0 > 3}
  • Closures have access to constants and variables defined outside them. Like a nested function in ActionScript, a Swift closure can access and modify the values of variables and constants in its surrounding context. For example, take a look at Listing 22-25.

Listing 22-25. Accessing Variables and Constants from the Surrounding Context
func sumArrayElements( arr: Array< Int > ) -> Int {
    var arrayTotal: Int = 0;


    func addArrayElement( item: Int ) {
        arrayTotal += item
    }


    for ( element ) in arr { addArrayElement( element ) }

    return arrayTotal
}

The function in this snippet takes an array of integers and returns the sum of the array’s elements. The nested closure defined inside it, addArrayElement, can read and modify arrayTotal, which lives in the context of the enclosing function, sumArrayElements.

Generics

Generic programming, also known as template metaprogramming in other parts of the programming world, allows you to write code that can be parameterized not just on the values it handles but also on the types it can work with. Sound too abstract? Read on.

A Generic Function in Swift

What would a function look like that takes types, as well as values, as parameters?2

Let us begin by declaring a function, called isContained, that takes an array and an object and returns true if the object is contained in the array, false otherwise.3 If you were to write the body of this function, it would probably do the following: iterate through the array elements until it finds a match with the given object or until it runs out of array elements. The function’s insides would be the same if you wanted it to look for a string in an array of strings, a number in an array of numbers, and so on. You can imagine the amount of duplicated code you would end up with if you were to provide an implementation of this function for every single type you need it to handle.

Swift spares you that need and instead delegates the job of providing type-specific implementations to the compiler, when you declare your function as in Listing 22-26.

Listing 22-26. Generic Function Syntax
func isContained<T>( arr : Array<T>, obj : T ) -> Bool

Translated into English, this reads: isContained is a function that takes an array of any type and an object of the same type. In the declaration of the function that type will be called T. In order to stop the compiler from complaining it doesn’t know what T is, we need to tell it that T is a type parameter (alias): this is what <T> after the name of the function stands for. It is conventional to use one-letter aliases such as T, but you can use any name you like, as long as it’s not an existing type or a reserved keyword.

Calling a Generic Function

How would you make a call to isContained? In many other languages you would need to explicitly provide the type you want to use in place of T at the time of the call. Due to the powers of the Swift compiler to infer types from context, however, you wouldn’t know you were calling a generic function (Listing 22-27).

Listing 22-27. Calling a Generic Function
let intArray = [45, 2, 6]
let objFound = isContained(intArray, obj: 2) // returns true

The Compiler Does the Dirty Work

What happens when you call a generic function is that the compiler creates code for you behind the scenes, which is compiled with the rest of your code. So, if you called isContained once with Int and once with String, the compiler would generate two implementations of isContained: one for handling each type. The code will only be generated at compile time and will not appear in your source files.

Generic Programming and Polymorphism

Both generic programming and polymorphism are tools and each tool has its time and place where it is a better choice than others. It is like having a flat-head and a cross-head screwdriver in your toolbox: none of them is better than the other in general, but you would prefer one or the other depending on what you want to achieve. Following are some of the situations in which you might prefer generics to polymorphism. And hope that polymorphism doesn’t take it personally.

  • When you need to be able to plug in unrelated types. <T> can be used as an alias for any type, while requiring an Object as a parameter means that what you pass in a function needs to descend from Object.4

  • When you need to enforce type safety across unrelated types. The ActionScript function declared in the last example can take an array of strings and look for an integer in it. No errors will be thrown, but using isContained like this would be somewhat wasteful. The generic declaration of isContained in Swift makes it clear that both the array and the object will need to be of the same type, whatever that type may be and the compiler will enforce that. This has the added benefit of documenting your intentions.

  • When you want to take advantage of compile-time error checking for unrelated types. A compiler or a linker error is worth about a hundred runtime errors. If inside the body of your generic function you tried to use a type-aliased argument in a way that its type doesn’t allow, the Swift compiler will let you know about it. Using an Object in ActionScript, on the other hand, allows you to do all sorts of things that may only turn out to be a problem at runtime.

  • When speed is important. With polymorphism, you can resolve which override method to call at runtime. And that’s one of the great things about polymorphism. When you don’t need polymorphic behavior, however, and just code that will run with types that are totally unrelated to one another, you are better off with generics speed-wise. As we saw earlier, the compiler expands generic functions for you, so a specific version of your function will be called without the need to work it out at runtime.

A Generic Type in Swift

In Swift, in addition to generic functions, you can define and use generic structures, classes, or enumerations. Listing 22-28 shows an example of a generic structure, called SimpleCollection. It has a member, mCollectables, which is an Array that can hold any type.

Listing 22-28. Declaring a Generic Type
struct SimpleCollection<T> {
    var mCollectables = [T]()


    mutating func collect(item: T) {
        mCollectables.append(item)
    }
}

You Use Generics in Swift Whether You Know It or Not

The container types in Swift, Array, Dictionary and Set, which you saw in Chapter 19 , are all generic types. This allows you to create collections of any type you need to.

In addition, the first version of Swift offered a number of global generic functions, such as sorted, which would sort the elements of a container. Most of these have been moved to methods of the container classes in Swift

Setting Constraints

Writing generic code for any type is quite flexible. Often, however, you want to restrict the types that a generic function or type allows because of what you need to do with them.

We will go back to our generic function example to illustrate this. This time let us add the function body (see Listing 22-29).

Listing 22-29. Generic Function Body
func isContained<T>( arr : Array<T>, obj : T ) -> Bool {
    for arrElement in arr {
        if arrElement == obj { return true }
    }


    return false
}

This line—if arrElement == obj—looks simple enough, but it has a glitch: not every type is guaranteed to have the equals operator (==) defined for it. Indeed, if you try to compile this code, the compiler will helpfully tell you that “Binary operator ‘==’ cannot be applied to two T operands.”

The way to ensure that only types that support the equals operator can be used with this function is to put a constraint on the type placeholder T and force it to conform to the Equatable protocol. This changes the signature of the function as shown in Listing 22-30.

Listing 22-30. Generic Function with a Constraint
func isContained<T: Equatable>( arr : Array<T>, obj : T ) -> Bool

More facts About Generics

We will close the section on generics with a brief list of points that are worth knowing:

  • You can have more than one type alias or placeholder (e.g., see Listing 22-31).

    Listing 22-31. Using More Than One Type Alias
    func mapTwoTypes<T, U>( instanceOfT: T, instanceOfU: U )
  • Type placeholders can be used in protocols too. In that case they are called associated types and are specified with the keyword typealias. This is what a protocol for our SimpleCollection structure might look like Listing 22-32.

    Listing 22-32. Using an Associated Type with a Protocol
    protocol SimpleCollectionProtocol {
        typealias T
        mutating func collect(item: T)
    }
  • Generic types (classes, structures, protocols, enumerations) can be extended.

  • There are even more elaborate ways of constraining type placeholders. You can constrain a type placeholder to conform to protocols or inherit certain classes, as in Listing 22-33.

    Listing 22-33. Constraining Type Placeholders
    func mapTwoTypes<T: CertainClass, U: CertainProtocol>
        ( instanceOfT: T, instanceOfU: U )

You can define even more specific constraints by using the where clause. Here is an example of that: let us define a protocol to use for collections and define an associated type in it, called ItemType. A generic function that compares two collections can then impose the following constraints on its arguments:

  • The two collections can be of different types (an Array and a Set, for example), but the items they contain must be of the same type.

  • The items in the collections must conform to the Equatable protocol, so that they can be compared.

The code in Listing 22-34 shows all this expressed in Swift

Listing 22-34. Constraining Type Aliases with the where Clause
protocol Collection { typealias ItemType }

func compareCollections
    <T: Collection, U: Collection
        where T.ItemType == U.ItemType, T.ItemType : Equatable>()
                ( collection1: T, collection2: U ) -> Int {
                // implementation goes here }

Summary

The new and different concepts presented in this chapter included the following:

  • Enumerations. A concept that ActionScript doesn’t have, but which comes in handy when you prefer to use sets of names, instead of hard-coded values.

  • Subscripts. You have been using subscripts (the array access operator []) your whole ActionScript life, but did you know you could define them too? You can in Swift.

  • Closures. Closures probably also looked familiar at the start. That is, until their syntax got weirder and weirder when we started taking advantage of the compiler’s ability to infer things from context.

  • Generics. Generics help you write code that takes not only different values as parameters but also different types. Not entirely possible in ActionScript.

This concludes our overview of the Swift programming language. Our hope is that it has offered you enough to not only kick-start your transition to it but also to give you the confidence that you know what happens behind the scenes of your code. In moments of doubt you can always return to this part of the book and use it as a reference or delve even deeper with Apple’s comprehensive manual, The Swift Programming Language.

In the last chapter we will talk business and cover Apple’s process for enrolling in a developer program and submitting apps to the App Store.

Footnotes

1 All this cutting down on syntax because of the cleverness of the compiler reminds me of an old, old joke from Gabrovo (Bulgaria), whose citizens are known to be a bit financially conservative…

A merchant goes to a distant town to sell wheat. Before heading back he walks into a post office and writes the following telegram to send to his wife: “Dear Ana, I sold the wheat profitably. Coming back tomorrow. Yours, Ivan.” He counts the words and decides to lower the cost of the telegram by skipping the first part, as his wife is obviously dear to him. So he shortens the telegram to: “Sold the wheat profitably. Coming back tomorrow. Yours, Ivan.” He looks at the text and thinks “But she knows I always sell with profit . . .” and shortens the telegram further: “Coming back tomorrow. Yours, Ivan.” Eager to lower his expenses, he reasons that of course his wife knows he is coming back shortly, why wouldn’t he?! And the text becomes: “Yours, Ivan.” The merchant looks at this final version, scratches his head, and says to himself, “Well, I am hers and she knows that.” Then he tears the telegram form and walks out of the post office.

2 It would be fair to say that the parameter analogy only goes so far: a conventional parameter to a function gets it value at runtime, while a type parameter must be resolved at compile time.

3 And let us pretend for a moment that array.indexOf() does not exist in Swift.

4 This is, admittedly, the case for all types in ActionScript.

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

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