"fatal error: array cannot be bridged from Objective-C"—Why are you even trying, Swift?

IosSwiftSwift Protocols

Ios Problem Overview


I have declared a Swift protocol:

protocol Option {
    var name: String { get }
}

I declare multiple implementations of this protocol—some classes, some enums.

I have a view controller with a property declared as so:

var options: [Option] = []

When I try and set this property to an array of objects that implement the Option protocol in another VC's prepareForSegue, I get a runtime error:

fatal error: array cannot be bridged from Objective-C

Why doesn't this work? The compiler has all the information it needs, and I don't understand what Objective-C has to do with it at all—my project contains only Swift files, and these arrays aren't coming in or out of any framework methods that would necessitate them being bridged to NSArray.

Ios Solutions


Solution 1 - Ios

I have found a solution. It is quite... unsatisfying, but it works. Where I set the array on the destination view controller I do:

destinationViewController.options = options.map({$0 as Option})

Solution 2 - Ios

> the compiler knows I'm passing in an Array of things that implement Option

You've let slip there a very revealing remark, which suggests the source of the issue. An "Array of things that implement Option" is not an Array of Option.

The problem is with the type of options back at the point where you create it (in prepareForSegue). You don't show that code, but I am betting that you fail to cast / type it at that point. That's why the assignment fails. options may be an array of things that do in fact happen to adopt Option, but that's not enough; it must be typed as an array of Option.

So, back in prepareForSegue, form your options like this:

let options : [Option] = // ... whatever ...

Now you will be able to assign it directly to destinationViewController.options.

Here's a quick test case (in a playground; I detest playgrounds, but they can have their uses):

protocol Option {
    var name : String {get}
}

class ViewController : UIViewController {
    var options : [Option] = []
}

enum Thing : Option {
    var name : String {
        get {
            return "hi"
        }
    }
    case Thing
}

let vc = ViewController()
let options : [Option] = [Thing.Thing]
vc.options = options // no problem

(I also tested this in an actual app with an actual prepareForSegue, and it works fine.)

Solution 3 - Ios

I was having the same problem and fixed it marking my protocol with @objc, in your case it would look like this

@objc protocol Option {
    var name: String { get }
}

Got the solution from this answer

Solution 4 - Ios

This one also works fine

destinationViewController.options = options.map{$0}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRobert AtkinsView Question on Stackoverflow
Solution 1 - IosRobert AtkinsView Answer on Stackoverflow
Solution 2 - IosmattView Answer on Stackoverflow
Solution 3 - IosJuanView Answer on Stackoverflow
Solution 4 - IosMykola DenysyukView Answer on Stackoverflow