Swift library using Promise, methods not visible in ObjC
fer662 opened this issue · 4 comments
I have a (private) swift pod which exposes a lot of methods that return Promise. I'm trying to use those methods from an ObjC codebase and they aren't visible from there. They do become visible if I use FBLPromise instead. I understand that's because Promise doesn't extend from NSObject, or something similar.
My idea for a workaround is making analog methods that call the underlying swift api and return the asObjCPromise() of them, and mark those methods with NS_SWIFT_UNAVAILABLE. Is there any better aproach or something i'm not seeing?
Edit: I missed the fact that NS_SWIFT_UNAVAILABLE is an ObjC thing. I also have a greater problem that is that these methods are part of a protocol, and providing alternatives doesn't fix the problem t hat the protocol cannot be seen from objc.
Am i to either fall back and use FBLPromises in my swift code, of always use my library from swift?
Hi Fernando,
To make any Swift func visible for ObjC you need an @objc
annotation.
Your idea of a duplicate method that you can then expose for ObjC is the right one.
Here's the pattern to follow in your Swift pod:
@objc(MyObjCClass)
public class MyClass: NSObject {
public func fooBar() -> Promise<String> {
// ...
return Promise("hello world")
}
@objc(objc_fooBar)
public func fooBar() -> Promise<String>.ObjCPromise<NSString> {
return fooBar().asObjCPromise()
}
}
Then use it from an ObjC target:
#import "MySwiftPod-Swift.h"
FBLPromise<NSString *> *fooBarPromise = [[MyObjCClass new] objc_fooBar];
If you have a lot of such methods in your class, you may consider moving them into a separate extension just to make the code look cleaner, like so:
public class MyClass: NSObject {
public func fooBar() -> Promise<String> {
// ...
return Promise("hello world")
}
// ...
}
// Objective-C interface.
public extension MyClass {
@objc(objc_fooBar)
public func fooBar() -> Promise<String>.ObjCPromise<NSString> {
return fooBar().asObjCPromise()
}
// ...
}
Let us know if you have any further questions.
Thanks.
I'm using this approach, but can't see how to handle errors in objective c.
I use the following in swift:
Test.client.createUser(firstName: "firstname", lastName: "lastname").then { _ -> Void in
assertionFailure("duplicate user created")
}.catch { error in
assert(error is RequestError, "RequestError returned")
let requestError = error as? RequestError
}
RequestError is a swift class visible in objective c
@objc(AGMRequestError)
public class RequestError
and in objective c
[client createUserWithFirstName:@"firstname" withLastName:@"lastname"] .then(^id(AGAccessToken *accessToken) {
XCTFail("duplicate user")
return nil;
})
.catch(^(NSError *error) {
AGMRequestError *requestError = (AGMRequestError *)error;
});
error is of type _SwiftNativeNSError, not AGMRequestError, so I can't access any of its details
Hi Matt,
Given your RequestError
has @objc
annotation, I assume it already derives from NSObject
? Any chance you derive it from NSError
instead? Then you can create instances of RequestError
in Swift as follows: RequestError(domain: "your domain", code: 42, userInfo: nil)
or with a custom constructor, and cast to AGMRequestError
in Objective-C.
By the way, you may also like declaring errors as enum
s in Swift and conforming to CustomNSError
protocol to be able to extract domain
, code
and userInfo
everywhere. Take a look at an example.
Thanks.
Hello,
Thank you for the suggestion. Yes RequestError derived from NSObject, and Error but I've changed it to just derive from NSError and that works - I can then cast to AGMRequestError in Objective-C.
I'll also consider conforming to CustomNSError instead. RequestError currently has a type, code, and description, but I could use userInfo for the type and description.