2 Replies
      Latest reply on Dec 21, 2015 1:47 AM by Cereopsis
      Cereopsis Level 1 Level 1 (0 points)

        I've found the answer to this one myself, not that I understand why however.

         

        Given a pure Swift object that conforms to a Swift protocol:

         

        protocol P {
            var name: String { get }
        }
        class PImpl: P {
            let name: String
            init(name: String) {
                self.name = name
            }
        }
        

         

        Run the following testcases:

         

            func unbridgableArrayOfPs() -> [P] {
                let pees = [PImpl(name: "A"), PImpl(name: "B"), PImpl(name: "C")]
                return pees
            }
        
            func arrayOfPs() -> [P] {
                return [PImpl(name: "A"), PImpl(name: "B"), PImpl(name: "C")]
            }
        
            func testUnbridgableArrayOfPs() {
                let p = unbridgableArrayOfPs() // Fails with fatal error: array cannot be bridged from Objective-C
                let result = p.map{$0.name}.joinWithSeparator("")
                XCTAssertEqual(result, "ABC")
            }
        
            func testArrayOfPs() {
                let p = arrayOfPs() // We get our P's
                let result = p.map{$0.name}.joinWithSeparator("")
                XCTAssertEqual(result, "ABC")
            }
        

         

        It would seem that returning the array directly without creating a temporary variable allows this to succeed, whereas the unbridgableArrayOfPs() function fails.

         

        Any thoughts?

         

        The other options are

             1. Annotate the protocol with @objc

             2. Change the return type of the array to the concrete implementation's type i.e PImpl

         

        1 Isn't going to work I believe if there are things like Swift enums in the mix

        2 From a design perspective, it's nice to keep things implementation agnostic where possible.

        • Re: Array cannot be bridged from Objective-C
          OOPer Level 8 Level 8 (6,155 points)

          The fatal error is caused by this line:

                  return pees
          
          

          In your `unbridgableArrayOfPs` method, the type of `pees` is inferred as [PImpl], and unfortunately the current Swift cannot convert [PImpl] to [P] .

          (If Swift runtime cannot handle this sort of conversion, Swift compiler should warn it. So, this issue should be considered as a bug.

          You can find Report Bugs link in almost all pages of Apple's dev site.)


          In your `arrayOfPs`, -- remember array literals are typeless in Swift --, the array literal is put as a return value, so its type is inferred from the return type of the method, the array literal is directly constructed as [P], no conversion occurs.


          Thus, another options are:

          - Annotate the type of `pees` explicitly

              let pees: [P] = [PImpl(name: "A"), PImpl(name: "B"), PImpl(name: "C")]
          

          - Annotate the type of the array literal explicitly

              let pees = [PImpl(name: "A"), PImpl(name: "B"), PImpl(name: "C")] as [P]
          

          - Convert [PImpl] to [P] manually

              let pees = [PImpl(name: "A"), PImpl(name: "B"), PImpl(name: "C")]
              let peesAsArrayP: [P] = pees.map{$0 as P}
              return peesAsArrayP
          
            • Re: Array cannot be bridged from Objective-C
              Cereopsis Level 1 Level 1 (0 points)

              Thanks, I can confirm that both the following work (as would the third, I'm sure but the other two look neater)

               

              let pees = [PImpl(name: "A"), PImpl(name: "B"), PImpl(name: "C")] as [P]
              

               

              let pees: [P] = [PImpl(name: "A"), PImpl(name: "B"), PImpl(name: "C")]
              

               

              I might file a bug anyway if I get a chance this week, even though the workaround is trivial.