Karl L.
Karl L.

Reputation: 51

Swift bridging header not exporting React Native types

I'm trying to create a native (iOS) module for a React Native app using Swift. I want to expose a method on the module that returns a promise, using functions of type RCTPromiseResolveBlock and RCTPromiseRejectBlock, both of which are declared in React Native's RCTBridgeModule.h header. However, my build fails with the message "Use of undeclared type..." for both of these types.

I had already created a bridging header (automatically using Xcode) for another purpose, so I believe importing React/RCTBridgeModule.h is all that I'd need to supply the types mentioned above. Presumably I've misconfigured something, as this doesn't fix the issue. I've tried setting up a fresh project and everything works as expected there, but I can't seem to find a difference that would cause my project's build to fail.

Some relevant configuration details:

Is my project misconfigured or have I otherwise missed something important?

Upvotes: 5

Views: 6263

Answers (1)

Andrew
Andrew

Reputation: 28539

This tutorial is pretty good at explaining how to set up a native module using Swift. It breaks everything down into steps and it is quite easy to follow.

https://teabreak.e-spres-oh.com/swift-in-react-native-the-ultimate-guide-part-1-modules-9bb8d054db03

  1. Setup
  2. How to expose a Swift class to JS
  3. How to expose static Swift data
  4. How to expose a Swift method
  5. How to expose a method with a callback
  6. How to expose a method as a Promise
  7. How to expose an Event Emitter
  8. How to extract your React Native Module

Clearly it is step 6 that you are looking to do.

Here is a code example. It is very similar to what is done in the above link. Your bridging header should look like this:

// <ProjectName>-Bridging-Header.h

#import "React/RCTBridgeModule.h"
#import "React/RCTEventEmitter.h"

You should have files named ModuleName.m and ModuleName.swift.

// ModuleName.m
#import "React/RCTBridgeModule.h"
#import "React/RCTEventEmitter.h"
@interface RCT_EXTERN_MODULE(ModuleName, NSObject)
// this is how we expose the promise to the javascript side.
RCT_EXTERN_METHOD(functionWithPromise: (RCTPromiseResolveBlock)resolve rejecter: (RCTPromiseRejectBlock)reject)
@end

// ModuleName.swift

@objc(ModuleName)
class ModuleName: NSObject {
  @objc
  func constantsToExport() -> [AnyHashable : Any]! {
    return ["projectName": "ModuleName"]
  }

  @objc
  static func requiresMainQueueSetup() -> Bool {
    return true
  }

  @objc
  func functionWithPromise(
    _ resolve: RCTPromiseResolveBlock,
    rejecter reject: RCTPromiseRejectBlock
  ) -> Void {
    if (//something bad happens) {
      let error = NSError(domain: "", code: 200, userInfo: nil)
      reject("ERROR_FOUND", "failure", error)
    } else {
      resolve("success")
    }
  }
}

Then on the Javascript side you can access it like this:

import { NativeModules } from 'react-native'

NativeModules.Counter.functionWithPromise()
    .then(res => console.log(res))
    .catch(e => console.log(e.message, e.code))

Upvotes: 7

Related Questions