Jan ATAC
Jan ATAC

Reputation: 1262

Loop over a NSDictionary with swift

I don't understand why this piece of Objective-C code:

id object = nil;
NSEnumerator *enumerator = ...;
while ((object = [enumerator nextObject])) {...}

can't be translated in Swift like this:

var key:AnyObject!
let enumerator:NSEnumerator = myNSDictionary.keyEnumerator()    
while ( (object = enumerator.nextObject()) ) {...}

I have this error :

Type '()' does not conform to protocol 'BooleanType'

Upvotes: 2

Views: 6254

Answers (2)

Martin R
Martin R

Reputation: 539705

In Objective-C, [enumerator nextObject] returns an object pointer which is nil when when all objects have been enumerated, and the value of an assignment

object = [enumerator nextObject]

is equal to the assigned value. In

while ((object = [enumerator nextObject])) { ... }

the while-block is executed as long as the expression compares unequal to 0, where 0 in this case is the null pointer constant which is usually written as NULL, or nil for Objective-C pointers. So that loop is equivalent to

while ((object = [enumerator nextObject]) != nil) { ... }

In Swift, an assignment does not have a value (it is a Void), therefore

while ( (object = enumerator.nextObject()) )

does not compile. You can forcibly cast it to a BooleanType to make it compile but that will crash at runtime.

enumerator.nextObject() returns AnyObject?, i.e. an optional object, which is nil when all objects have been enumerated. The proper way to test the return value for nil is optional binding:

let enumerator = myNSDictionary.keyEnumerator()
while let key = enumerator.nextObject() {
    print(key)
}

(Of course, bridging the NSDictionary to a Swift Dictionary and then using the Swift enumeration methods, as suggested in the other answer, is a sensible alternative.)

Upvotes: 6

Woodstock
Woodstock

Reputation: 22926

You should enumerate over a dictionary as follows (which is described here in further detail):

Test dictionary declaration:

var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

Enumerate both key and value:

for (airportCode, airportName) in airports {
    print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow

Enumerate just keys:

for airportCode in airports.keys {
    print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR

Enumerate just values:

for airportName in airports.values {
    print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow

Maybe there was a deeper reason you are choosing to use NSEnumerator, but if not the above is alot more elegant from a Swift point of view.

Upvotes: 9

Related Questions