Using Obj-C completion block in Swift

元气小坏坏 提交于 2019-12-10 18:15:31

问题


In Objective-C, I have a completion block class defined as:

File.h

typedef void (^MYCompletionBlock)(BOOL success, NSDictionary *result, NSError *error);

Then, in a Swift file, I try to use the completion block as follows:

Swift.swift

class MyClass: NSObject{
     ...

     func MyFunction() -> Void {
          ...
          objcMethod(param1, withCompletion: {(MYCompletionBlock) -> Void in
               if (success){ // Error:"Use of unresolved identifier 'success'"
               }
          }
          ... 
     }
     ...
}

But, I keep getting an error: "Use of unresolved identifier 'success'".

I've tried the following as well:

objcMethod(param1, withCompletion: {(success:Bool, result: NSDictionary, error:NSError) -> Void in
     if (success){ // Error:"Cannot convert value of type '(Bool, NSDictionary, NSError) -> Void' to expected argument type "MYCompletionBlock!" 
     }
}

Can somebody help me understand how to correctly specify a Obj-C completion block in Swift?


回答1:


Given that your closure doesn't specify nullability qualifiers (where they almost certainly are optional), one can safely assume that your Objective-C API has not been audited for nullability. Thus, Swift will treat pointers as implicitly unwrapped optionals. Furthermore, nowadays the NSDictionary is mapped to a [NSObject : AnyObject] Swift dictionary.

Thus, it would be:

obj.objcMethod(param) { (success: Bool, result: [NSObject : AnyObject]!, error: NSError!) in
    if success {
        // do something
    }
}

Or, as Kobi points out, you can let the compiler infer the types:

obj.objcMethod(param) { success, result, error in
    if success {
        // do something
    }
}

Note, you don't have to remember this yourself. You can leverage Xcode's code completion as you enter the code. So, type enough to match the method name and when it matches objcMethod, then hit enter:

When you get to MYCompletionBlock, hit enter again, and it will show you the correct signature:


If this Objective-C method was my own class, I would audit it for nullability. So, for example, let's assume the param is optional, the closure is required, and the result and error were optional, you might define it like so:

NS_ASSUME_NONNULL_BEGIN

typedef void (^MYCompletionBlock)(BOOL success, NSDictionary * _Nullable result, NSError * _Nullable error);

@interface MyObject : NSObject

- (void)objcMethod:(NSDictionary * _Nullable)param1 withCompletionHandler:(MYCompletionBlock)completionHandler;

@end

NS_ASSUME_NONNULL_END

And, if that was the case, your Swift code would call it like so:

obj.objcMethod(param) { (success: Bool, result: [NSObject : AnyObject]?, error: NSError?) in
    if success {
        // do something
    }
}

Or, again, just let the compiler infer the types for you (but this time they'd be inferred as optionals that are not implicitly unwrapped):

obj.objcMethod(param) { success, result, error in
    if success {
        // do something
    }
}



回答2:


You shouldn't specify types for the completion block parameters, as some types defer between Swift and Objective C (e.g. BOOL is actually ObjCBool in Swift). This should work:

objcMethod(param1) { (success, result, error) in
    if (success){ 
        // Do something
    }
}


来源:https://stackoverflow.com/questions/37356631/using-obj-c-completion-block-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!