Ron
Ron

Reputation: 1640

Swift 1.2 cast [AnyObject?] to [AnyObject]

If you look at this function:

typealias JSONDictionary = [String: AnyObject]

private func allIdentifiersInJson(json: [JSONDictionary], key: String?) -> [AnyObject] {
        if let identifier = key {
            let identifiers = json.map{ $0[identifier] } //this returns [AnyObject?]
            return identifiers as? [AnyObject] ?? [] // this crashes the compiler in Release with Optimizations
        }
        return []
    }

How would you cast an [AnyObject?] to an [AnyObject]

In Release config with optimizations enabled, I get this error during compilation:

Bitcast requires both operands to be pointer or neither
  %548 = bitcast i64 %547 to %objc_object*, !dbg !10033
LLVM ERROR: Broken function found, compilation aborted!

Upvotes: 1

Views: 135

Answers (4)

MirekE
MirekE

Reputation: 11555

One way would be to use flatMap:

let l: [AnyObject?] = [1, 2, 3, 4, 5, 6, nil, 7]
let m = l.flatMap { $0 } // Returns [1, 2, 3, 4, 5, 6, 7] as [AnyObject]

(tested only in Swift 2)

Upvotes: 3

Stuart
Stuart

Reputation: 37043

You probably want to be using something like this instead:

private func allIdentifiersInJson(json: [JSONDictionary], key: String?) -> [AnyObject] {
    if let key = key {
        return json.map { $0[key] }.filter { $0 != nil } as! [AnyObject]
    }
    return []
}

Because we are using filter to remove all instances of nil, we can use the forced cast to [AnyObject] without fear of a crash.

Or, as pointed out by @MirekE in their answer, flatMap is even more succinct:

return json.flatMap { $0[key] }

Which only returns non-nil results, and removes the need for any casting.

Upvotes: 1

Patrick Lynch
Patrick Lynch

Reputation: 2782

Expanding on what is in my comment above, if you use the following, your app will crash if identifier is not a key in the dictionary:

let identifiers = json.map { $0[ identifier ]! }

So, what you might want to do is filter that first, then perform the mapping knowing that you can force unwrap because you've already checked for that key:

let identifiers = json.filter({ $0[ identifier ] != nil }).map { $0[ identifier ]! }

But if it were me, I'd want to get rid of the force unwrap altogether for total safety, even when someone else or even yourself comes back to this code a week later and monkeys with it by taking out the filter:

let identifiers = json.map({ $0[ identifier ] ?? NSNull() }).filter { !($0 is NSNull) }

Now you are safely unwrapping with the nil coalescing operator (??) and providing a default object (NSNull), then filtering out those default objects.

Upvotes: 1

Ron
Ron

Reputation: 1640

This function will not crash the compiler:

private func allIdentifiersInJson(json: [JSONDictionary], key: String?) -> [AnyObject] {
    if let key = key {
        let identifiers = json.map{ $0[key]! } //this returns [AnyObject]
        return identifiers ?? []
    }
    return []
}

Upvotes: 0

Related Questions