stephanmantel
stephanmantel

Reputation: 1827

How to bridge React Native Promise to Swift

Hi fellow software enthousiasts,

I am currently working on a React native project for which I need to add some logic which has been written in swift. I am able to trigger a basic swift function through the bridging to Objective C an then to Swift.

The problem occurs when I try to do something with promises. The page I describing this is clear on the Objective C part for Promises and also for bridging to Swift, but not so on promises to swift: https://facebook.github.io/react-native/docs/native-modules-ios.html

This is what I have:

Project-Bridging-Header.h

#import <React/RCTBridgeModule.h>

MyLoginBridge.m

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_REMAP_MODULE(MyCustomLoginJSName, MyLoginModule, NSObject)

RCT_EXTERN_REMAP_METHOD(loginWithEmail,
                    resolver:(RCTPromiseResolveBlock)resolve
                    rejecter:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(testMethod)

@end

MyLoginModule.swift

import Foundation

@objc(TripleASDKModule)
class TripleASDKModule: NSObject {

  @objc
  func loginWithEmail(resolver resolve: RCTPromiseResolveBlock,  rejecter reject: RCTPromiseRejectBlock) -> Void {
    resolve("This method is troublesome")
  }

  @objc func testMethod() -> Void {
    print("This Does appear")
  }
}

When i trigger the testMethod, the print is shown in Xcode, so that swift code is executed. But when I call the loginWithEmail method, I get the infamous red React Native error screen saying:

Exception 'resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject is not a recognized Objective-C method.' was thrown while invoking loginWithEmail on target MyCustomLoginJSName with params (
    30,
    31
)

And for the sake of completeness, the Javascript side:

const loginWithEmail = () => NativeModules.TripleA.loginWithEmail()
    .then(result => console.log(result));

I tried almost all variations of RCT_EXTERN_REMAP_METHOD and the like I could find, both with and without Remapping repeating the name, etc. So if this problem sound familiar, or you could guide me in the right direction, please do so, any help is appreciated.

Upvotes: 24

Views: 17498

Answers (3)

Koen.
Koen.

Reputation: 26999

Taken from the answers at Got "is not a recognized Objective-C method" when bridging Swift to React-Native; the fact that it doesn't work, is because of the difference in the first argument labels.

To make it work with your initial code you should write the first argument of your Swift to be without a name, like this:

@objc
func loginWithEmail(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
    //   the trick  ^
    resolve("This method is no longer troublesome")
}

Upvotes: 24

pwcremin
pwcremin

Reputation: 755

Adding so that the complete solution is shown

.m - note that the resolve parameter is not "named"

RCT_EXTERN_METHOD(loginWithEmail: (RCTPromiseResolveBlock)resolve
                                  rejecter:(RCTPromiseRejectBlock)reject)

.swift - same as @Koen

@objc func loginWithEmail(_ resolve: @escaping RCTPromiseResolveBlock,
                            rejecter reject: @escaping RCTPromiseRejectBlock ) -> Void {}

Upvotes: 14

stephanmantel
stephanmantel

Reputation: 1827

I did eventually come up with a solution. It is not exactly like React native people intended it I believe, but it works. So i can continue and maybe someone else is set on the right track with my solution. Although, please post the way it is supposed to be.

So I decided to start with the Objective-C way first. So I created a .h file for my module.

MyLoginBridge.h

#import <React/RCTBridgeModule.h>

@interface MyLoginBridge : NSObject <RCTBridgeModule>
@end

Then alter the .m file

#import "MyLoginBridge.h"
#import "MyProject-Swift.h" // Include the swift header manually

@implementation MyLoginBridge

RCT_EXPORT_MODULE(MyCustomLoginJSName);

RCT_EXPORT_METHOD(loginWithEmail:(RCTPromiseResolveBlock)resolve   rejecter:(RCTPromiseRejectBlock)reject)
{
    // Manually init the module and call swift function
    MyLoginModule* module = [[MyLoginModule alloc] init];
    [module loginWithEmailWithResolver:resolve rejecter:reject];
}

@end

The swift file and the bridging header remained the same. This works.

Upvotes: 0

Related Questions