3

I am a complete beginner when it comes to iOS programming. I have a React Native application, and I have made a native bridge to a Swift module. What I want to achieve is to be able to launch a native ViewController page from React Native, and then close the ViewController and send events containing data back to React Native.

I have edited in the following files:

Brygga.m (My ObjC bridge between React-Native and Swift)
Bridge-Test-Bridging-Header.h (Bridging-Header file)
ConnectingFile.swift (My Swift classes and methods)

I have the following files:

According to the tutorials I have read, to send events from native to React-Native, we have to use RCTEventEmitter. But I cannot initiate a ViewController from a RCTEventEmitter subclass, so I have made two classes.

One is a subclass of UIViewController, and one is subclass of RCTEventEmitter. I have successfully launched my ViewController, but when trying to send the event data from the ViewController back to React Native through my RCTEventEmitter class; I get the error

"Bridge is not set. This is probably because you've explicitly synthesized the bridge in Brygga, even though it's inherited from RCTEventEmitter."

ConnectingFile.swift

import Foundation

@objc(Connect)
class Connect: UIViewController, MiSnapWorkflowViewControllerDelegate{
  
  func didFinishWithResults(resultsDictionary: [String : Any]) {
    let event: brygga = Brygga()
    brygga.Finished(data: "Hello")
  }
 
  @objc func startViewController() {
    DispatchQueue.main.async {
      let vc = MiSnapWorkflowViewController.init(with: [.passport, .selfie])
      
      vc.delegate = self
      let navigationController = UINavigationController(rootViewController: vc)
      navigationController.modalPresentationStyle = .fullScreen
      
      let topViewController = UIApplication.shared.keyWindow?.rootViewController
      topViewController?.present(navigationController, animated: true, completion:     nil)
      
    }
  }
 
}

@objc(Brygga)
class Brygga: RCTEventEmitter {
 
  
  @objc
  func Finished(data: String) {
    sendEvent(withName: "onFinished", body: ["data": data])
  }
  
  override func supportedEvents() -> [String]! {
    return ["onFinished"]
  }

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

Brygga.m

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

@interface RCT_EXTERN_MODULE(Brygga, RCTEventEmitter)

RCT_EXTERN_METHOD(Finished)

@end

@interface RCT_EXTERN_MODULE(Connect, RCTViewManager)

RCT_EXTERN_METHOD(goToNative)

@end

BridgeTest-Bridging-Header.h

#import <React/RCTBridgeModule.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTEventEmitter.h>
#import <React/RCTRootView.h>
#import <React/RCTUtils.h>
#import <React/RCTConvert.h>
#import <React/RCTBundleURLProvider.h>

#import <React/RCTViewManager.h>

I think it may have something to do with how I initiate the RCTEventEmitter class from my ViewController. This guy has a similiar problem but I couldn't understand the solution

Implement RCTEventEmitter in Swift but receive exception for bridge is not set

1 Answer 1

7

I eventually solved it. Here is the solution for sending events from your other classes that do not extend RCTEventEmitter, for instance if you like me want to send events from a class of yours that extends a UIViewController.

Your Swift bridge should consist of the following:

  • Bridge.m
  • RNEventEmitter.swift
  • MyViewController.swift (or any other class of yours)

Bridge.m

@interface RCT_EXTERN_MODULE(RNEventEmitter, RCTEventEmitter)

RCT_EXTERN_METHOD(supportedEvents)

@end

RNEventEmitter.swift

@objc(RNEventEmitter)
open class RNEventEmitter: RCTEventEmitter {

  public static var emitter: RCTEventEmitter!

  override init() {
    super.init()
    RNEventEmitter.emitter = self
  }

  open override func supportedEvents() -> [String] {
    ["onFinished", "onPending"]      // etc.
  }
}

MyViewController.swift

  func sendEvent(_ name: String, body: [String : Any]) {
    RNEventEmitter.emitter.sendEvent(withName: name, body: body)
  }

Then from just anywhere sendEvent("onFinished", "Native process has finished!")

Of course you will need to receive this on the React Native side as well. I can as well show how you can do that;

App.js

import {NativeModules, NativeEventEmitter} from 'react-native';

const {RNEventEmitter} = NativeModules;

const eventEmitter = new NativeEventEmitter(RNEventEmitter);

  eventEmitter.addListener('onFinished', res =>
    console.log(res.data),
  );
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.