Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift library using Promise, methods not visible in ObjC #78

Closed
fer662 opened this issue Nov 1, 2018 · 4 comments
Closed

Swift library using Promise, methods not visible in ObjC #78

fer662 opened this issue Nov 1, 2018 · 4 comments

Comments

@fer662
Copy link

fer662 commented Nov 1, 2018

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?

@shoumikhin
Copy link
Contributor

shoumikhin commented Nov 3, 2018

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.

@ghost ghost closed this as completed Nov 3, 2018
@matcoope
Copy link

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

@shoumikhin
Copy link
Contributor

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 enums in Swift and conforming to CustomNSError protocol to be able to extract domain, code and userInfo everywhere. Take a look at an example.

Thanks.

@matcoope
Copy link

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.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants