7 Replies
      Latest reply: Jan 9, 2017 3:58 PM by eskimo RSS
      Dogstarsuper Level 1 Level 1 (0 points)

        guys,

        I created a swift class like this:

         

        class SwiftClass
        {
        
            override init()
            {
                super.init()
            }
        
            static func Test1(closure:()->Void){
                closure()
            }
        
            static func Test2(array:[()->Void]){
                for closure in array {
                    closure()
                }
            }
        }
        
        

         

        Both 2 methods wok fine from swift file.

         

        SwiftClass.Test1(closure:{print("Test1")})
        SwiftClass.Test2(array:[{print("Test2_closure1")},{print("Test2_closure2")}])
        
        

         

        The function Test1 work fine from objective-c file too.

        [SwiftClass Test1WithClosure:^{
                NSLog(@"SwiftClass Test1") ;
            }] ;
        
        

         

        but Test2 can't be used from objective-c file,the automatically generated -Swift.h file didn't declare it at all.

        SWIFT_CLASS("_TtC17ArrayClosuresTest10SwiftClass")
        @interface SwiftClass : NSObject
        - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
        + (void)Test1WithClosure:(void (^ _Nonnull)(void))closure;
        @end
        
        

         

        Is it possible to call Test2 from objective-c file?

        • Re: Passing an array of closures to a swift method form objectiv-c file
          QuinceyMorris Level 7 Level 7 (3,215 points)

          If you explicitly declare the array-based method as "@objc", you'll get the following compilation error message: "method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C".

           

          Since you don't get this error with an array of (say) Int, and with a non-array closure parameter, this suggests the compiler could make an @objc-compatible method, but chooses not to try. Or, there may be a technical reason why this array/closure combination can't be represented in Obj-C.

           

          As a workaround, you could change the Test2 parameter type to [AnyObject], or specify the parameter type as NSArray,  and cast individual elements as the closure type.

           

          Either way, it's probably worth a bug report, in case this is something that is really supposed to work.

            • Re: Passing an array of closures to a swift method form objectiv-c file
              Dogstarsuper Level 1 Level 1 (0 points)

              THX,

              I tried the [AnyObject] and NSArray workaround ,

              static func Test3(array:[AnyObject]){
                      for closure in array {
                          (closure as! ()->Void)()
                      }
                  }
              
              

              It build successlly but I got a runtime error

              "Could not cast value of type '__NSGlobalBlock__' (0x1188e9040) to '(()) -> ()' (0x1188ef030)."

              Is there any mistake in the casting?

                • Re: Passing an array of closures to a swift method form objectiv-c file
                  eskimo Apple Staff Apple Staff (6,665 points)

                  The reason why your first attempt didn’t work is that Swift closures and Objective-C blocks are not identical (although they are closely related).  You can get your test2(array:) method exposed to Objective-C by adopting @convention(block), as shown here:

                  typealias Callback = @convention(block) () -> Void
                  
                  class Test : NSObject {
                      class func test2(array: [Callback]) {
                          …
                      }
                  }
                  

                  Or, as you’ve already discovered, using NSArray.  However, the cast required to call the callbacks still fails.  If you expand the above to this:

                  @objc class func test2(array: [Callback]) {
                      for item in array {
                          item()
                      }
                  }
                  

                  you get:

                  fatal error: NSArray element failed to match the Swift Array Element type
                  

                  And, as you’ve noted, in the NSArray case, where you have to do an explicit cast, you get a more focused error (even if you use @convention(block)).

                  Unfortunately I was unable to find a way around this, which brings me to the same conclusion as QuinceyMorris: this is definitely worth a bug.

                  Please post your bug number, just for the record.

                  Share and Enjoy

                  Quinn “The Eskimo!”
                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                  let myEmail = "eskimo" + "1" + "@apple.com"