简体   繁体   English

React Native:如何将事件从 iOS (Swift) 发送回 JavaScript?

[英]React Native: How can I send events from iOS (Swift) back to JavaScript?

I am working on a project where I want to integrate React-Native into a native Swift app.我正在做一个项目,我想将 React-Native 集成到本机 Swift 应用程序中。 To make sure both sides are aware of state, I've made a 'message bus', A mechanism through which events can be passed from Javascript to native, and vice versa.为了确保双方都知道 state,我制作了“消息总线”,这是一种可以将事件从 Javascript 传递到本地的机制,反之亦然。

This works like a charm when sending an event from JS to iOS;当从 JS 向 iOS 发送事件时,这就像一个魅力; it gets received, parsed and my Swift code knows exactly what to do.它被接收、解析并且我的 Swift 代码确切地知道该怎么做。 Sending an event from Swift to Javascript seems a lot harder - and poorly documented - as I find myself stuck on an error:将事件从 Swift 发送到 Javascript 似乎要困难得多——而且记录不充分——因为我发现自己陷入了一个错误:

terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error when sending event: RCTCrossPlatformEventBus.Event with body: {   "name" : "Authenticated",   "data" : "{\"firstName\":\"1\",\"email\":\"34\",\"lastName\":\"2\",\"password\":\"3\"}" }. RCTCallableJSModules is not set. This is probably because you've explicitly synthesized the RCTCallableJSModules in RCTEventEmitter, even though it's inherited from RCTEventEmitter.'

This error seems to be common as there are a lot of stack overflow questions and GitHub issues to be found on it.这个错误似乎很常见,因为上面有很多堆栈溢出问题和 GitHub 问题。 However, nearly all of them date back from ±5 years ago, and simply don't help me with my issue any more.但是,几乎所有这些都可以追溯到 ±5 年前,并且不再帮助我解决我的问题。 Below, I'm listing my implementation.下面,我列出了我的实现。 If anybody could provide me with some guidance on how to solve this issue, it would be highly appreciated.如果有人可以就如何解决此问题向我提供一些指导,我们将不胜感激。

RCTCrossPlatformEventBus.m RCTCrossPlatformEventBus.m

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


@interface RCT_EXTERN_MODULE(RCTCrossPlatformEventBus, RCTEventEmitter)
    RCT_EXTERN_METHOD(supportedEvents)
    RCT_EXTERN_METHOD(processHybridEvent: (NSString *)name) // this receives JS events
@end

RCTCrossPlatformEventBus.swift RCTC跨平台事件总线.swift

@objc(RCTCrossPlatformEventBus)
open class RCTCrossPlatformEventBus: RCTEventEmitter {
        
    override init() {
        super.init()
    }
    
    static let appShared = RCTCrossPlatformEventBus()
    
    @objc
    public override static func requiresMainQueueSetup() -> Bool {
        return true
    }
    
    /// Processes a received event received from hybrid code
    /// - Parameters:
    ///   - json: the json encoded string that was sent
    @objc func processHybridEvent(_ json: String) {
        print("Swift Native processing event: \(json)")
        DispatchQueue.main.async {
            var jsonObject: [String: Any]?
            if let jsonData = json.data(using: .utf8), let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as? [String:Any] {
                jsonObject = dict
            }
            NotificationCenter.default.post(name: .RCTCrossPlatformEventBusEvent, object: self, userInfo: jsonObject)
        }
    }

    /// Posts an event to both the hybrid code
    /// - Parameters:
    ///   - json: the json encoded string that will be sent
    @objc func postEvent(json: String) {
        self.sendEvent(withName: "RCTCrossPlatformEventBus.Event", body: json)
    }
    
    open override func supportedEvents() -> [String]! {
        return ["RCTCrossPlatformEventBus.Event"]
    }
    
    open override func constantsToExport() -> [AnyHashable : Any]! {
        return [:]
      }
}

App_Bridging_Header.h App_Bridging_Header.h

#ifndef ArchitectureDemo_Bridging_Header_h
#define ArchitectureDemo_Bridging_Header_h

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

Then, in Javascript (Typescript actually)然后,在 Javascript (实际上是打字稿)

import { NativeModules, NativeEventEmitter } from 'react-native'
import { BehaviorSubject, Observable } from 'rxjs'
const { CrossPlatformEventBus } = NativeModules;

const eventEmitter = new NativeEventEmitter(CrossPlatformEventBus)

class RNCrossPlatformEventBus {

    // we set up a private pipeline for events we can post to
    private postableEventBus = new BehaviorSubject<string>('')
    // and then expose it's observable for everyone to subscribe to
    eventBus = this.postableEventBus.asObservable()

    constructor() {
        eventEmitter.addListener('RCTCrossPlatformEventBus.Event', (body) => {
            this.processEventFromNative(body)
        })
    }

    postEvent(json: string) {
        this.postableEventBus.next(json)
        CrossPlatformEventBus.processHybridEvent(json);
    }

    processEventFromNative(jsonString: string) {
        this.postableEventBus.next(jsonString)
        console.log(`React-Native received event from native ${jsonString}`)
    }
}

export default new RNCrossPlatformEventBus()

I resolved my problem in the meantime and am posting the answer here for future reference.我同时解决了我的问题,并在此处发布答案以供将来参考。

As it turns out, initializing my RCTCrossPlatformEventBus - as I do in my swift file - Is an invalid operation.事实证明,初始化我的RCTCrossPlatformEventBus - 就像我在 swift 文件中所做的那样 - 是一个无效操作。 React-Native already initialize this, so rather than creating a singleton myself, I just had to override it's initializer like so: React-Native 已经初始化了它,所以我不必自己创建 singleton ,我只需要像这样覆盖它的初始化程序:

open class RCTCrossPlatformEventBus: RCTEventEmitter {
    
    public static var shared: RCTCrossPlatformEventBus?
    
    override init() {
        super.init()
        RCTCrossPlatformEventBus.shared = self
    }
    
    ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM