Working with Foundation Types
In Objective-C, a number of familiar types are implemented as classes as part of the Foundation framework: NSString, NSNumber, NSArray, NSDictionary, and NSSet. Because Cocoa was built for Objective-C, you will often run into these classes when working with the Cocoa APIs. The good news is that Apple has made transitioning between the Swift and Foundation (Objective-C) counterparts relatively painless. They are toll-free bridged, meaning that there is minimal computational cost in converting between the two.
Basic bridging
Swift types are automatically bridged to their Foundation counterparts:
let string = "Howdy" let objcString: NSString = string
The reverse is not true, however:
let swiftString: String = objcString // Error!
Instead, you must explicitly cast it using as:
let swiftString: String = objcString as String // Ok!
Another class that you may see is NSNumber. Because Foundation collections can only store objects, in order to store numbers they must be represented by an object. NSNumber is the class that Objective-C programmers use for this task. Swift numbers also bridge easily with NSNumber:
let objcNumber: NSNumber = 3 let swiftNumber = objcNumber as Int
Bridging with collections
Bridging with collections is similar, but a wrinkle emerges when casting from a Foundation array back to Swift:
let array = [1, 2, 4, 8] let objcArray: NSArray = array // So far so good... let swiftArray: [Int] = objcArray as [Int] // Error!
You may be surprised to learn that Foundation collections can hold any kind of object – that is, the collection’s contents do not have to be of the same type! You will see the Swift type AnyObject used with these collections, like this: [AnyObject].(If you are familiar with Objective-C, AnyObject has the same meaning as id.)
The solutions to this problem are similar to unwrapping optionals: there are safe and unsafe paths. The unsafe path is to use as!, the forced cast operator:
let swiftArray: [Int] = objcArray as! [Int]
As with forced unwrapping, if the type cannot be cast successfully your app will crash. If you are certain that the type is correct, such as when the value is coming from a known API, this is a reasonable assumption to make.
If you are not so certain, you should use the optional type casting operator as?, which will evaluate to nil if the values cannot be safely cast:
if let swiftArray: [Int] = objcArray as? [Int] { ... }
This situation is most commonly seen with Cocoa APIs using NSDictionary: it is typical for the keys to all be NSStrings, but the types of the values commonly differ depending on the key. We will further discuss how to handle these untyped collections safely in Chapter 28.
Suppose you were working with an Objective-C class that supplied a dictionary. When Swift imports the class, it does a basic level of conversion, but it does not know what type the method actually returns, so it is shown as [NSObject : AnyObject]!:
class NSProcessInfo: NSObject { ... var environment: [NSObject : AnyObject]! { get } ... }
To work with this API you first need to know the actual types contained in the dictionary, which can usually be found in the documentation. You will then need to safely cast the result to Swift types:
let processInfo = NSProcessInfo() if let environment = processInfo.environment as? [String : String] { if let path: String = environment["PATH"] { println("Path is: \(path)") } }
It is important to remember that Swift strings and collections are value types and the Foundation types are all reference types. While Swift’s compiler can enforce the constant-ness of an array, with NSArray the same array object may be referenced by many parts of an application.
The Foundation classes we have discussed so far are all immutable, meaning that they cannot be changed – equivalent to being defined with Swift’s let. Each of them has a mutable subclass: NSMutableArray, NSMutableString, and so forth. This has less of an impact on Swift code, but it is important to watch out for if you are working with a significant body of Objective-C code. Because it is a reference type, an instance of NSMutableArray could be changed by any code that has a reference to it.