Chapter    3

Working with Strings and Collections

Chapter 2 introduced the basic structure of Swift and explained the different types, mutability versus immutability, and notable syntax changes, among other things. This chapter builds on that foundation of knowledge, focusing on creating and manipulating strings and collections of values.

Working with Strings

The Swift String value type is bridged seamlessly to Objective-C’s NSString class, meaning that any NSString method can be called on a String value, and a String value can be used with any API that expects an NSString instance. However, this does not mean that there is complete interchangability between String and NSString. For example, String methods such as toInt()(), computed properties such as endIndex, and global functions such as countElements() will not work with an NSString instance.

Following an examination of the salient differences between Swift’s String type and Objective-C’s NSString and NSMutableString classes, Table 3-1 provides several examples that compare NSString and NSMutableString methods and techniques with Swift String equivalents (see Chapter 5 for additional coverage of the conditional syntax used in some of the examples).

Much as in Objective-C, strings in Swift can be created in a variety of ways, including as empty strings, literal strings, and using format strings. Remember that the mutability of a String value in Swift is determined by whether it is initialized as a variable (var) or constant (let), versus by class selection in Objective-C (NSMutableString and NSString).

As in Objective-C and mentioned in the last chapter, a String type in Swift is actually an ordered collection (i.e., sequence) of Unicode characters (specifically, Character values in Swift). Exploring String’s definition (such as by command + clicking on a String type declaration in Xcode) reveals that String is actually a simple struct type accompanied by a series of extensions:

struct String {
  init()
}

extension String : CollectionType {
// ...

Clicking into CollectionType’s definition shows that it is a protocol, and so this extension adopts and conforms to the CollectionType protocol, which (clicking through to its definition) conforms to the _CollectionType protocol, which is the input type expected by the global countElements() function. Therefore, countElements() can be called, passing a String type, and it will return the count of Character values that make up the string:

let abcString = "ABC"
countElements(abcString) // 3

Note  A protocol in Swift is—much as in Objective-C—simply a contract. By adopting a protocol in Swift, a type is agreeing to implement the protocol’s requirements. Chapter 8 will cover protocols in full.

countElements() should normally suffice when the need to get a String value’s length arises. Remember from the last chapter, however, that the Character type represents a sequence of extended grapheme clusters that are combined to produce a single human-readable character. countElements() counts Character values, not the individual clusters, which could be one or more per each Character value. Reading further into the extension that adopts the CollectionType protocol will disclose how, then, to get the length of a String value in terms of the count of individual clusters:

extension String : CollectionType {
  struct Index : BidirectionalIndexType, Comparable, Reflectable {
    func successor() -> String.Index
    func predecessor() -> String.Index
    func getMirror() -> MirrorType
  }
  var startIndex: String.Index { get }
  var endIndex: String.Index { get }
  subscript (i: String.Index) -> Character { get }
  func generate() -> IndexingGenerator<String>
}

Even though much of this syntax may be new to you at this point, the lines defining the vars startIndex and endIndex should look familiar. These are computed properties that return the position of the index before the first cluster, and position of the index after the last cluster, respectively (computed properties will be further covered in Chapter 7):

let abcString = "ABC"
abcString.startIndex // 0
abcString.endIndex   // 3

Notice that, because endIndex is the position of the index after the last character, it also happens to be equal to the length of the string. This is not always the case, however:

let circledStar: Character = "u{2606}u{20DD}" // 9781484204078_unFig03-01.jpg

circledStar is a single Character value made up of two clusters: a white star (U+2606) and a combining enclosing circle (U+20DD). Let's create a String from circledStar and compare the results of countElements() and endIndex:

let circledStarString = "(circledStar)"
countElements(circledStarString) // 1
circledStarString.endIndex // 2

All that said, Swift’s exact counterpart to Objective-C’s NSString length property is String’s utf16Count computed property, because both are based on the number of 16-bit code units, not the number of Unicode extended grapheme clusters. It is therefore possible that two String values made up using different clusters are considered equal and countElements() returns the same result; however, their endIndex and utf16Count properties will be different. In the following example, the character “é” is created using a single cluster (U+00E9) in string1, and two clusters (U+0065 for “e” and U+0301 for the combining acute accent) in string2:

let string1 = "The e acute character is u{E9}" // "é"
let string2 = "The e acute character is u{65}u{301}" // "é"
println(countElements(string1) == countElements(string2)) // Prints "true"
println(string1.endIndex == string2.endIndex) // Prints "false"
println(string1.endIndex) // Prints "26"
println(string2.endIndex) // Prints "27"
println(string1.utf16Count == string2.utf16Count) // Prints "false"
println(string1.utf16Count) // Prints "26"
println(string2.utf16Count) // Prints "27"

The Swift String type also has an isEmpty computed property, which can be used to determine if a string is, well, empty. Similar to Objective-C’s –[NSString hasPrefix] and –[NSString hasSuffix] methods, Swift String types also have hasPrefix() and hasSuffix() methods.

String has several init() methods that can be used to create strings from integer value types, as well as a variety of init() methods that use format strings (wherein the format string arguments can either be provided as a comma-separated list or in an array). String also has a toInt() method that will attempt to convert a String value to an Int value. Because this attempt may fail, an optional Int? is returned. Remember to unwrap the optional before use. Swift does not currently offer a toDouble() method; however, an example of how to convert a String value to a Double value (by casting to an NSString and using NSString API) is provided in Table 3-1.

Swift strings can be concatenated using the + and += operators. A Character value can be appended to a String value using the append() method.

Note  A character literal assigned to a variable or constant will be implicitly inferred to be of type String. Explicitly cast a character as type Character as necessary.

Remember that NSString instances in Objective-C are passed by reference unless explicitly copied, whereas String values in Swift are always passed by copy.

Tip  Press option + p to type the character “π.”

Table 3-1. Comparing Objective-C NSString and NSMutableString methods and techniques to Swift String equivalents

 

Objective-C

Swift

Create

NSString *string1 = @"Hello world!";

NSMutableString *string2 = [NSMutableString new];

NSMutableString *string3 = [@"" mutableCopy];

let string1 = "Hello world!"

var string2 = String()

var string3 = ""

Introspect and interpolate

NSLog(@"%lu", (unsigned long)string1.length); // Prints "12"

NSLog(@"%i", !string2.length); // Prints "1"

NSLog(@"%i", [string1 hasPrefix:@"Hello"]); // Prints "1"

NSLog(@"%i", [string1 hasSuffix:@"earth!"]); // Prints "0"

CGFloat C = 9.42f;

NSInteger d = 3;

NSLog(@"π is equal to %.2f", C / d); // Prints "π is equal to 3.14"

NSLog(@"π is equal to %.2f", 3.14159265358979323846); // Prints "π is equal to 3.14"

import Foundation

println(string1.utf16Count)
// Prints "12"

println(countElements(string1))
// Prints "12"

println(string2.isEmpty)
// Prints "true"

println(string1.hasPrefix("Hello"))
// Prints true

println(string1.hasSuffix("earth!"))
// Prints false

let C = 9.42

let d = 3.0

println("π is equal to (C / d)")
// Prints "π is equal to 3.14"

let π = String(format: "%.2f", 3.14159265358979323846)

println("π is equal to (π)") // Prints "π is equal to 3.14"

Compare

if ([string2 isEqualToString:string3]) {

NSLog(@"string2 equals string3");

}

// Prints "string2 equals string3"

if string2 == string3 {

println("string2 equals string3")

}

// Prints "string2 equals string3"

Convert

NSString *fiveString = [@5 stringValue]; // "5"

NSInteger five = [fiveString integerValue];

let fiveString = "(5)" // "5"

let five = fiveString.toInt()!

let pi = Double((π as NSString).doubleValue) // 3.14

Copy and mutate

NSMutableString *string4 = [string1 mutableCopy];

[string4 appendFormat:@" Am I alone?"];

NSLog(@"%@", string1); // Prints "Hello world!"

NSLog(@"%@", string4); // Prints "Hello world! Am I alone?"

NSMutableString *string5 = string4;

[string5 replaceCharactersInRange:NSMakeRange(13, 10) withString:@"How do you like me now"];

if ([string4 isEqualToString:string5]) {

NSLog(@"%@", string5);

}

// Prints "Hello world! How do you like me now?"

var string4 = string1

string4 += " Am I alone?"

println(string1) // Prints "Hello world!"

println(string4) // Prints "Hello world! Am I alone?"

var string5 = string4

let startIndex = advance(string5.startIndex, 13)

string5.replaceRange(startIndex..<string5.endIndex, with: "How do you like me now?")

if string4 != string5 {

println(string5)

}

// Prints "Hello world! How do you like me now?"

 

NSMutableString *tempHigh = [@"85" mutableCopy];

NSMutableString *tempLow = [@"70" mutableCopy];

NSString *degreeF = @"u2109";

[tempHigh appendString:degreeF];

[tempLow appendString:degreeF];

NSLog(@"High/Low: %@/%@", tempHigh, tempLow); // Prints "High/Low: 85°F/70°F"

var tempHigh = "85"

var tempLow = "70"

let degreeF: Character = "u{2109}"

tempHigh.append(degreeF)

tempLow.append("u{2109}" as Character)

println("High/Low: (tempHigh)/(tempLow)") // Prints "High/Low: 85°F/70°F"

Creating Tuples and Collections

Swift defines two formal collection types: Array and Dictionary. Although collections of type NSSet, NSMutableSet and NSCountedSet can be created and worked with in Swift using Foundation API, there are no direct counterparts to these classes in the Swift standard library.  Although not a formal collection type, Swift does add a new type for which there is no counterpart in Objective-C that can be used for grouping multiple values into a single compound value, called a tuple.

Creating Tuples

Tuples, although new to Objective-C developers transitioning to Swift, are by no means a new programming construct. Tuples can be found in several other languages, including Python, Haskell, and C#. Tuples in Swift are intended for temporarily grouping related values, and Apple advises to not use tuples to create complex data structures. Common uses include returning multiple values from a function (see Chapter 6 for details) and to test multiple values in a switch case statement (covered in Chapter 5). For data structures that are more complex or will persist beyond a temporary scope, use a class or structure instead (covered in Chapter 7).

Tuple values are ordered and can be composed of the same or differing types. To create a tuple, enclose a comma-separated list of values in parentheses:

var httpStatus = (200, "OK")

The previous example relies on type inference and the tuple created is of type (Int, String). You can also explicitly state the type of a tuple, as in the following example, which adheres to good form by making the variable an optional, because we do not assign a value:

var httpStatus: (Int, String)?

Because tuples are ordered lists, you can access tuple elements at their numerical order index (beginning with 0) using dot syntax:

var httpStatus = (200, "OK")
let code = httpStatus.0
let message = httpStatus.1
println("The code is (code) and the message is (message).")

More succinctly, tuples can also be decomposed into separate stored values using another tuple:

let (code, message) = httpStatus
println("The code is (code) and the description is (message).")

You can also optionally assign a name to one or more tuple elements during creation, and subsequently access named elements by either name or index.

let httpStatus = (200, message: "OK")
println("The code is (httpStatus.0) and the message is (httpStatus.message).")

If you do not need to access a tuple element during decomposition, you can ignore it by using an underscore character. You’ll see similar usage elsewhere in Swift, generally encouraged to aid performance and code clarity:

let (code, _) = httpStatus
println("The code is (code).")

Tuples can also be nested:

let (score, (firstName, _)) = (100, ("Scott", "Gardner"))
println("(firstName) received a score of (score).")

Table 3-2 demonstrates how tuples in Swift can reduce code and increase clarity, compared with how you might perform the same task in Objective-C.

Table 3-2. Comparing Objective-C and Swift tuple approach to performing a common task

Objective-C

NSUInteger code = 200;

NSString *message = @"OK";

NSLog(@"The code is %lu and the message is %@.", (unsigned long)code, message);

Swift

let (code, message) = (200, "OK")

println("The code is (code) and the message is (message).")

Creating Arrays

Like Objective-C, arrays in Swift store ordered collections of values in a zero-indexed list (i.e., the first item is at index 0). Unlike Objective-C, in which arrays can only store objects, Swift arrays can store values of any type, and as Apple’s Swift language guide states repeatedly, an array should store values of the same type.

As with any variable or constant, a Swift array’s type can be inferred based on its contents if set during declaration, or it can be explicitly stated. Swift’s literal syntax for creating an array is the same as Objective-C’s literal syntax but without the @ prefix.

Table 3-3 compares creating arrays in Objective-C and Swift. Notice the lack of using the const keyword in Objective-C, as doing so does not make the array any more immutable.

Table 3-3. Creating arrays in Objective-C and Swift

 

Objective-C

Swift

Mutable array

NSMutableArray *planets = [@[@"Mercury", @"Venus", @"Earth"] mutableCopy];

var planets = ["Mercury", "Venus", "Earth"]

Immutable array

NSArray *planets = @[@"Mercury", @"Venus"];

let planets = ["Mercury", "Venus"]

Note  For what it’s worth, -[NSObject mutableCopy] facilitates using a shorter syntax for creating a mutable array than using +[NSMutableArray arrayWithObjects:] in Objective-C, and performance is not noticeably different until about 2,000,000 iterations.

In the previous table, the Swift array type is inferred based on the type of the values stored in the array. A Swift array’s type can also be explicity stated during declaration. The syntax for doing so is to enter the type enclosed in square brackets, or specify the type is of Array<SomeType>, although the former is preferred. Specifying an array is optional (using ? postfix) indicates that the array can be nil, in addition to being empty or containing values:

var intArray1: [Int] = [1, 2, 3]
var intArray2: Array<String>?
var intArray3: [Int]?

You can create an empty array using Array’s initializer syntax with either type declaration approach. Table 3-4 compares creating mutable empty arrays in Objective-C and Swift.

Table 3-4. Creating mutable empty arrays in Objective-C and Swift

Objective-C

Swift

NSMutableArray *arrayOfAnyObjects = [NSMutableArray array];

var stringArray = Array<String>()

var intArray = [Int]()

var myClassArray = [MyClass]()

Note  There are of course additional ways to instantiate an empty array in Objective-C, such as [[NSMutableArray alloc] init], [NSMutableArray new], and [[NSMutableArray alloc] initWithCapacity:<#(NSUInteger)#>]. Because our focus is on transitioning to and learning Swift, Objective-C examples will typically be a single common use-case versus being exhaustive.

It is possible to store different types in a Swift array; however, doing so may produce unexpected results with type inference. For example, an array of Ints and Strings has its type inferred to be NSArray, but add an NSObject instance to it and its type is then inferred to be [NSObject], and yet add an instance of a custom Swift reference type such as the MyClass type we created earlier, and the array’s type is once again inferred to be NSArray. What should be mentioned, however, is that if you need an array that can hold multiple types, you can do so by explicitly declaring a variable array of type [AnyClass] or [Any], the former being an array capable of holding any reference type, the latter capable of holding any type whatsoever; AnyClass is a typealias for AnyObject:

var anyArray = [Any]()
var anyClassArray = [AnyClass]()

Swift arrays also have an initializer to create an array of a specified count with each element having the specified repeatedValue:

var studentScores = [(String, Int)](count: 3, repeatedValue: ("Name", 100))
println(studentScores) // Prints "[(Name, 100), (Name, 100), (Name, 100)]"

Creating Dictionaries

Apple defines a Swift dictionary as being an unordered container that stores multiple values of the same type, wherein each value is associated with a unique identifier key.

Objective-C dictionaries can only store objects, and can use any object for the key as long as it conforms to the NSCopying protocol. Swift dictionaries can store any type and use any type for the key as long as it conforms to the Hashable protocol (protocols are covered in Chapter 8). All of Swift’s basic value types can be used as dictionary keys, including Character, String, Int, Double, Float, and Bool. Also, members of a new type of enumeration in Swift (in which its members are not specifically assigned a value) can be used as dictionary keys (enumerations are covered in Chapter 7).

The literal syntax for creating a Swift dictionary uses square brackets just like arrays, and has the same key: value syntax as found in Objective-C’s literal syntax, sans the @. Table 3-5 compares creating dictionaries in Objective-C and Swift.

Table 3-5. Creating dictionaries in Objective-C and Swift

 

Objective-C

Swift

Mutable dictionary

NSMutableDictionary *planets = [@{@1: @"Mercury", @2: @"Venus", @3: @"Earth"} mutableCopy];

var planets = [1: "Mercury", 2: "Venus", 3: "Earth"]

Immutable dictionary

NSDictionary *planets = [@{@1: @"Mercury", @2: @"Venus", @3: @"Earth"} mutableCopy];

let planets = [1: "Mercury", 2: "Venus", 3: "Earth"]

A Swift dictionary’s type can be inferred by the type of keys and values assigned to the dictionary during declaration, as in the preceding table, or they can be explicitly stated during declaration using either literal [KeyType: ValueType] or full Dictionary<KeyType, ValueType> syntax, with the literal syntax being preferable:

var stringDictionary1: [Int: String] = [1: "One", 2: "Two"]
var stringDictionary2: Dictionary<Int, String>?
var stringDictionary3: [Int: String]?

You can create an empty dictionary using Dictionary’s initializer syntax with either type declaration approach, or as may be commonly seen, by initially assigning a key/value pair (thereby relying on type inference to establish the key and value types) and then setting the dictionary to an empty dictionary ([:]). Table 3-6 compares creating mutable empty dictionaries in Objective-C and Swift.

Table 3-6. Creating mutable empty dictionaries in Objective-C and Swift

Objective-C

Swift

NSMutableDictionary *dictionaryOfAnyObjects = [NSMutableDictionary dictionary];

var stringDictionary1 = [Int: String]()

var stringDictionary2 = Dictionary<Int, String>()

var stringDictionary3 = [1: ""]

stringDictionary3 = [:]

It is possible to create a dictionary in Swift that is assigned multiple types of keys and values during declaration; however, doing so not only changes the dictionary’s type to NSDictionary, but it also makes the dictionary immutable. Although the Swift standard library does not enable you to declare a Dictionary type that uses different (hashable) key types and multiple value types (that is, [Hashable: Any] won’t work), it does enable you to create a dictionary of a specified key type that can store multiple value types:

var dictionaryOfAnyTypes = [Int: Any]()
var dictionaryOfAnyClassTypes = [String: AnyClass]()

Mutability

Swift’s approach to mutability of tuples and collections is logical and consistent, yet somewhat different from Objective-C. The next section will cover modifying tuples and collections. It is important first to gain a solid understanding of how mutability is applied to Swift tuples, arrays, and dictionaries.

As with all stored values in Swift, the mutability of a Swift tuple, array, or dictionary is determined during declaration, not by choice of class as it is in Objective-C (NSArray/NSMutableArray, NSDictionary/NSMutableDictionary).

Swift tuples and collections declared as variables can be reassigned to new tuples or collections, respectively, of the same type. Arrays and dictionaries–but not tuples–declared as variables can have items added to and removed from them. Arrays and dictionaries declared as constants cannot be reassigned, nor can they have items added or removed.

Mutability of the values within a tuple or collection is applied as follows. Value types passed into a tuple or collection are passed by copy, inheriting the mutability of the tuple or collection. So a variable or constant value type assigned to a variable tuple or passed in to a variable array is copied in as a mutable variable. Similarly, a variable or constant value type passed in as the value of a key/value pair to a constant dictionary is copied in as an immutable constant. Table 3-7 summarizes the mutability of tuples, arrays, and dictionaries in Objective-C and Swift.

Table 3-7. Summary of tuple, array, and dictionary mutability in Objective-C and Swift

Table3-7.jpg

Multidimensional Tuples and Collections

Swift tuples and collections can contain nested tuples and collections, just like arrays and dictionaries in Objective-C:

var tupleWithArrayWithTuplesAndDictionaryWithArrays = (([(1, 2, 3), (4, 5, 6)]),[1: ["a", "b", "c"], 2: ["d", "e", "f", "g"]])
println(tupleWithArrayWithTuplesAndDictionaryWithArrays.0) // Prints "[(1, 2, 3), (4, 5, 6)]"
println(tupleWithArrayWithTuplesAndDictionaryWithArrays.1) // Prints "[1: [a, b, c], 2: [d, e, f, g]]"
println(tupleWithArrayWithTuplesAndDictionaryWithArrays.1[2]![3]) // Prints "g"

Working with Tuples and Collections

The following three subsections will cover most aspects of working with tuples, arrays, and dictionaries. Enumerating collections, due to the relationship of these use cases to controlling program flow, is deferred to Chapter 5.

Working with Tuples

A tuple cannot be sorted, nor can it have items added to or removed from it. A variable tuple can be modified, and its value type elements can also be modified. Use the same dot syntax to change the value of a tuple element as you would to access it, but each value can only be changed to a new value of the same type. Value types passed into a variable tuple can be modified, whereas value types passed into a constant tuple cannot. This is regardless of whether the value type being passed in is itself declared as a variable or constant, because—remember—value types are always passed by copy:

var spade = ("Spade", symbol: "♣")
let club = ("Club", symbol: "♠")
spade.symbol = "♠"
var suits = (spade: spade, club: club)
suits.club.symbol = "♣"
println(suits.club) // Prints "(Club, ♣ )"
println(club) // Prints "(Club, ♠ )"

Reference types are passed by reference, and therefore, their mutability—that is, the mutability of their properties—within a tuple is the same as outside the tuple (Chapter 7 will cover reference types in depth, including the syntax used to create the class in the following example):

class MyClass {
  var myVariableProperty = "Can be changed"
}

let myClass1 = MyClass()
let myClass2 = MyClass()
var classTuple = (myClass1, myClass2)
classTuple.0.myVariableProperty = "Changed"

A tuple can also be declared to store value types of Any (value or reference types) or AnyClass (reference types only), and subsequently have those members assigned and reassigned:

var suits: (Any, Any, Any, Any)
suits = ("Spade", "Club", "Heart", "Diamond")
suits.0 = ["Spade": "♠ "]
suits.1 = ["Club": "♣ "]
suits.2 = ["Heart": "♥ "]
suits.3 = ["Diamond": "♦ "]

Working with Arrays

Table 3-8 compares several ways to work with arrays in Objective-C and Swift, following highlights of the similarities and differences between the two.

Swift uses the same subscript syntax as Objective-C to access array items. NSArray instances have count, firstObject, and lastObject properties, and, similarly, Array values have count, first, and last computed properties. Ranges of items can be retrieved from Objective-C arrays using –[NSArray subarrayWithRange:], whereas Swift enables using range operators with subscript syntax to retrieve or replace a subarray of values from another array. When replacing a range of values, the replacement array does not need to be the same length as the range it is replacing. There are two kinds of range operators: the closed range operator (..., as in a...e) includes the range of values from the start value to and including the end value (a to and including e), and the half-open range operator (..<, as in a..<e) includes the range of values from the start value up to but not including the end value (a up to but not e). Range operators will be mentioned again in the next chapter on operators and receive additional coverage in Chapter 5 for their use in program control flow statements.

To check if an array is empty in Objective-C, the count property can be checked to see if it is greater than 0; however, Swift includes the isEmpty computed property for this purpose.

Finding items in an NSArray is made possible by a variety of indexOf... methods such as -[NSArray indexOfObject:], coupled with using subscript syntax or –[NSArray objectAtIndex:] to access the found item. Swift simplifies this task with the find() method, which searches the array passed in for the value passed in, and returns an optional wrapping the value if found, or nil if not. Pairing up with Objective-C’s –[NSArray filteredArrayUsingPredicate:], which takes an NSPredicate instance, Swift includes includes a filter() method that takes a closure (similar to a block in Objective-C) that returns a Bool, which can be used to return an array matching the conditions of the filter. Closures are a powerful feature Swift with a variety of different usage syntax options. For completeness, examples of closure usage with arrays will be included in Table 3-8 using the most compact version of syntax possible. This syntax may seem slightly esoteric at first, but once understood by the community-at-large of Swift developers, it should quickly become the de facto standard. See Chapter 6 for elaborate coverage of closures and the variety of syntax options. One tip that may help make sense of the syntax in these examples before consulting Chapter 6 is that numbers prefixed with $ refer to a closure’s arguments (e.g., $0 is the first argument).

Arrays can be sorted in Objective-C using a comparator block, selector, function, or descriptor. Swift array can be sorted using sorted() and sort() (the former returns a new array and the latter mutates the array it is called on), and also map() and reduce() methods, all of which take advantage of closures to reorder, transform, and combine an array’s items, respectively.

Adding to and replacing items in an NSMutableArray instance in Objective-C can be done via methods that include –[NSMutableArray addObject:], -[NSMutableArray insertObjectAtIndex:], -[NSMutableArray replaceObjectsAtIndexes:withObjects:], and variations. Similar methods are available for Array in Swift, and additionally, Swift enables use of the + and += operators to concatenate two arrays.

Note  A value itself cannot be added to a Swift array using the + and += operators. Use the append() method instead, or optionally enclose the value(s) in an array to use as the right operand of a + or += operator.

Removing items from an NSMutableArray instance in Objective-C is accomplished using a variety of remove... methods such as –[NSMutableArray removeObjectAtIndex:]. Swift matches these methods with removeAtIndex() and others.

Table 3-8 includes setup code at the top for use in the examples that follow.

Table 3-8. Comparing Objective-C NSArray and NSMutableArray methods and techniques to Swift Array equivalents

 

Objective-C

Swift

 

NSMutableArray *array = [@[@1, @2, @3, @4, @5] mutableCopy];

var array = [1, 2, 3, 4, 5]

Inspect

NSLog(@"array is empty? %@", array.count ? @"NO" : @"YES"); // Prints "NO"

NSLog(@"%lu", (unsigned long)array.count); // Prints "5"

println(array.isEmpty)
// Prints "false"

println(array.count)
// Prints "5"

Access

NSNumber *itemAtIndex3 = array[3]; // 4

NSArray *rangeOf0to3 = [array subarrayWithRange:NSMakeRange(0, 3)]; // (1, 2, 3)

NSArray *rangeOf0through3 = [array subarrayWithRange:NSMakeRange(0, 4)]; // (1, 2, 3, 4)

let itemAtIndex3 = array[3] // 4

let rangeOf0to3 = array[0..<3] // [1, 2, 3]

let rangeOf0through3 = array[0...3] // [1, 2, 3, 4]

Find

NSInteger index = [array indexOfObject:@5];

NSNumber *foundItem = array[index]; // 5

let foundItem = find(array, 5)! // 4

Filter

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self > 3"];

NSArray *greaterThan3 = [array filteredArrayUsingPredicate:predicate]; // (4, 5)

let greaterThan5 = array.filter { $0 > 3 } // [4, 5]

Sort

NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:NO];

NSArray * reverseSortedArray = [[array sortedArrayUsingDescriptors:@[descriptor]] mutableCopy]; // (5, 4, 3, 2, 1)

let reverseSortedArray = sorted(array, >) // [5, 4, 3, 2, 1]

array.sort { $0 > $1 } // [5, 4, 3, 2, 1]

Append

[array addObject:@7]; // (1, 2, 3, 4, 5, 7)

array = [1, 2, 3, 4, 5]

array += [7] // [1, 2, 3, 4, 5, 7]

// array.append(7) could also have been used

Insert

[array insertObject:@0 atIndex:0]; // (0, 1, 2, 3, 4, 5, 7)

array.insert(0, atIndex: 0) // [0, 1, 2, 3, 4, 5, 7]

Replace

array[6] = @6; // (0, 1, 2, 3, 4, 5, 6)

array[6] = 6 // [0, 1, 2, 3, 4, 5, 6]

Remove

[array removeLastObject]; // (0, 1, 2, 3, 4, 5)

array.removeLast() // [0, 1, 2, 3, 4, 5]

Map

NSMutableArray *dollarsArray = [NSMutableArray array];

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {

[dollarsArray addObject:[NSString stringWithFormat:@"%@%@", @"$", obj]];

}];

NSLog(@"%@", dollarsArray); // Prints "("$0", "$1", "$2", "$3", "$4", "$5")"

let dollarsArray = array.map { "$($0)" } // ["$0", "$1", "$2", "$3", "$4", "$5"]

Reduce

int totalOfArray;

for (NSNumber *i in array) {

totalOfArray += [i intValue];

}

NSLog(@"%i", totalOfArray); // Prints "15"

let totalOfArray = array.reduce(0, +) // 15

Working with Dictionaries

Objective-C and Swift dictionaries work in similar ways, yet have some differences that will be noted first, followed by comparative examples in Table 3-9.

Swift dictionaries have isEmpty and count computed properties just like arrays, the latter of which is also available in Objective-C as a property of NSDictionary and NSMutableDictionary instances.

Objective-C’s –[NSDictionary allKeys] and –[NSDictionary allValues] methods that return NSArray instances (order unspecified) have corresponding keys and values computed properties for Swift dictionaries. The return value for these computed properties in Swift, however, is of a collection type that can be enumerated just like a regular array (see chapter 5 for details), or new arrays can be created from the collections using syntax that will be demonstrated in Table 3-9. The collections returned for both the keys and values computed properties are ordered ascending by key, as is the order of key-value pairs when a Dictionary value is printed out.

Swift uses the same subscript syntax as Objective-C to access dictionary items. Values can be retrieved and added using subscript syntax in both Objective-C and Swift. Retrieving a value from a Swift dictionary is handled differently. Because a lookup may not find a value for the key provided, the returned value is an optional of the value if found, or nil if not. Similar to –[NSMutableDictionary setObject:forKey:] in Objective-C (and –[NSMutableDictionary setValue:forKey:], except that it requires an NSString instance for the key), Swift dictionary values can also be changed using the updateValue(forKey:) method. Unlike in Objective-C, however, updateValue(forKey:) returns an optional of the old value, and if a value was not found for the key provided, a new value for that key is added and nil is returned.

Although Objective-C does not allow it, a key-value pair can be removed from a Swift Dictionary type by using subscript syntax to set the value to nil for the key. Both languages offer methods to remove items by key or remove all items; however, Swift does not currently offer a counterpart to Objective-C’s –[NSMutableDictionary removeObjectsForKeys:].

Table 3-9. Comparing Objective-C NSDictionary and NSMutableDictionary methods and techniques to Swift Dictionary equivalents

 

Objective-C

Swift

 

NSMutableDictionary *dictionary = [@{@1: @"One", @2: @"Two", @3: @"Three"} mutableCopy];

var dictionary = [1: "One", 2: "Two", 3: "Three"]

Inspect

NSLog(@"%@", dictionary.count ? @"NO" : @"YES"); // Prints "NO"

NSLog(@"%lu", (unsigned long)dictionary.count); // Prints "3"

println(dictionary.isEmpty) // Prints "false"

println(dictionary.count) // Prints "3"

Access

NSLog(@"%@", dictionary[@1]); // Prints "One"

NSArray *dictionaryKeys = [dictionary allKeys]; // (3, 1, 2)

NSArray *dictionaryValues = [dictionary allValues]; // (Three, One, Two)

println(dictionary[1]) // Prints "One"

let dictionaryKeys = [Int](dictionary.keys) // [1, 2, 3]

let dictionaryValues = [String](dictionary.values) // ["One", "Two", "Three"]

Insert

dictionary[@4] = @"Five";

NSLog(@"%@", dictionary); // Prints "(3 = Three, 2 = Two, 1 = One, 4 = Five)"

dictionary[4] = "Five"

println(dictionary) // Prints "[1: One, 2: Two, 3: Three, 4: Five]"

Update

dictionary[@4] = @"Four"; // (3 = Three, 2 = Two, 1 = One, 4 = Four)

dictionary[4] = "Six" // [1: "One", 2: "Two", 3: "Three", 4: "Six"]

if let oldValue = dictionary.updateValue("Four", forKey: 4) {

println("The value for key 4 was changed from (oldValue) to (dictionary[4]!)")

}

// Prints "The value for key 4 was changed from Six to Four"

Remove

[dictionary removeObjectForKey:@4];

NSLog(@"%@", dictionary); // Prints "(3 = Three, 2 = Two, 1 = One)"

dictionary[4] = nil

println(dictionary) // Prints "[1: One, 2: Two, 3: Three]"

Syntax Reference

Figures 3-1 to 3-5 provide syntax summaries for creating characters, strings, tuples, arrays, and dictionaries in Swift. The same optional declarations (? and !) can be used as demonstrated in the variable and constant syntax summary in Figure 2-3 (see Chapter 2), and are thus omitted here. Italicized text indicates optional components, which are omitted in successive examples of identical usage for each type.

9781484204078_Fig03-01.jpg

Figure 3-1. Syntax for creating characters in Swift

9781484204078_Fig03-02.jpg

Figure 3-2. Syntax for creating strings in Swift

9781484204078_Fig03-03.jpg

Figure 3-3. Syntax for creating tuples in Swift

9781484204078_Fig03-04.jpg

Figure 3-4. Syntax for creating arrays in Swift

9781484204078_Fig03-05.jpg

Figure 3-5. Syntax for creating dictionaries in Swift

Summary

In this chapter you learned how to create and work with strings and collections in Swift, as compared with performing these tasks in Objective-C. Although there are many similarities between the two languages, it is important to not only understand the syntactic differences but also to strive toward taking full advantage of the powerful new capabilities provided in Swift.

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

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