What is toll-free bridging, and how do you convert between Swift and Foundation types?

2.9.1 · L1 · Interview

Predict first

What does this print?

let a: NSArray = [1, "x"]
print(a as? [Int] as Any, a as? [Any] as Any)
  • nil Optional([1, x])
  • Optional([1]) Optional([1, x])
  • nil nil

Mental model. Bridging is a plug adapter between two countries’ sockets: the same electricity, but the plug shape and the safety rules change.

Many Swift value types map to a Foundation class: String to NSString, Array to NSArray, Dictionary to NSDictionary, Set to NSSet, Data to NSData, Date to NSDate, URL to NSURL, and the numeric types to NSNumber. In Swift you convert with as (always succeeds) and recover a Swift type with as? or as! from Any / AnyObject.

The Objective-C id arrives in Swift as Any, and an untyped NSArray as [Any]. If the Objective-C header carries lightweight generics (NSArray<NSString *> *) you get [String] directly; without them you must cast element types yourself, and a mismatch makes as? return nil for the whole collection.

A conditional cast to [Int] fails if any element is not an Int. It does not drop the bad ones.

Common traps

  • Casting a mixed NSArray to [Int]. One wrong element makes the whole cast nil.
  • Assuming a bridged value is still a value. The Foundation side may be a mutable object with identity.

They might follow up with

  • Why “toll-free”? The two sides share one memory layout, so conversion needs no copying for the classic Core Foundation types.
  • What does as? cost on a collection? It checks every element, so it is O(n).

Related questions

  • 2.9.2 Why does NSNumber confuse Bool, Int and Double, and how do you read untyped values safely?
  • 2.9.3 What does bridging cost, and where does it show up in a profile?
  • 2.9.4 Which Foundation types are value types in Swift, which are classes, and what happens to mutable ObjC types?

Why does NSNumber confuse Bool, Int and Double, and how do you read… →