Skip to content

Support React Native 0.83.x New Architecture on iOS via Interop Layer#126

Open
DGuang21 wants to merge 5 commits intovoize-gmbh:mainfrom
DGuang21:feature/support-new-arch
Open

Support React Native 0.83.x New Architecture on iOS via Interop Layer#126
DGuang21 wants to merge 5 commits intovoize-gmbh:mainfrom
DGuang21:feature/support-new-arch

Conversation

@DGuang21
Copy link

@DGuang21 DGuang21 commented Jan 27, 2026

👋 Hi there! Our team is also implementing a solution that integrates Kotlin Multiplatform with React Native. Although our usage is slightly different (we embed RN pages within KMP), the core challenge remains the same: enabling React Native to invoke KMP code effectively.

We experimented with generating KMP interfaces based on the objects declared by the New Architecture, but found the process to be overly complex. Furthermore, React Native 0.83.1 (New Architecture) still retains support for legacy Modules. After extensive testing, we discovered that on Android, the bridge code generated by reakt-native-toolkit works directly even under the New Architecture. However, the implementation on iOS differs, causing it to fail in automatically locating the legacy Modules.

To address this, we implemented hack to ensure compatibility with React Native 0.83.1. The primary goal of this PR is to resolve the visibility issue within the iOS Interop Layer for RN 0.83.1.

Usage Changes

The logic is straightforward. If you are using the New Architecture or React Native 0.83.x and above, you can configure the following in build.gradle.kts:

ksp {
    arg("reakt.native.toolkit.kmpFrameworkName", "ComposeApp")
}

How it works

After executing the code generation task, Swift code compatible with the Interop Layer pattern will be generated in: build/generated/ksp/metadata/commonMain/resources/reaktNativeToolkit/iosNewArch

This directory will contain:

Swift files corresponding to the Module Namespaces.

RCTBridgeModule.m and Types.swift.

Installation Step

You simply need to go to your iosApp in Xcode:

Right-click and select Add files to "xxx".

Select the folder build/generated/ksp/metadata/commonMain/resources/reaktNativeToolkit/iOSInteropLayer.

Choose "Create folder references" (Reference files in place).

Stability & Compatibility

Aside from this step, no other manual handling is required. The bridge code generation, TS bridges, and direct references work as usual. We have also added compatibility for Flow.

We have tested and confirmed the stability of all solutions, with the exception of the Flow type. The app we maintain is open source, and you can verify the relevant references and implementations there. Our App

Generated Code Examples

//
//  NativeFeedbackModule.swift
//
//  Generated by reakt-native-toolkit for React Native 0.83+ new architecture.
//  Do not modify manually.
//

import Foundation
import React
import ComposeApp

@objc(FeedbackModule)
class NativeFeedbackModule: NSObject {

    @objc static func moduleName() -> String {
        return "FeedbackModule"
    }

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

    /// sendFeedback
    @objc(sendFeedback:resolve:reject:)
    func sendFeedback(_ param: String,
                  resolve: @escaping RCTPromiseResolveBlock,
                  reject: @escaping RCTPromiseRejectBlock) {

        // Call Kotlin bridge function
        FeedbackModuleBridgeKt.sendFeedbackFromJS(
            jsonParam: param,
            onSuccess: { result in
                resolve(result)
            },
            onError: { error in
                reject("RN_FEEDBACKMODULE_ERROR", error, nil)
            }
        )
    }

    /// increment
    @objc(increment:reject:)
    func increment(_ resolve: @escaping RCTPromiseResolveBlock,
                reject: @escaping RCTPromiseRejectBlock) {

        // Call Kotlin bridge function
        FeedbackModuleBridgeKt.incrementFromJS(
            onSuccess: { result in
                resolve(result)
            },
            onError: { error in
                reject("RN_FEEDBACKMODULE_ERROR", error, nil)
            }
        )
    }

    /// count (Flow)
    @objc(count:previous:resolve:reject:)
    func count(_ subscriptionId: String,
                  previous: String?,
                  resolve: @escaping RCTPromiseResolveBlock,
                  reject: @escaping RCTPromiseRejectBlock) {

        // Call Kotlin bridge function for Flow
        FeedbackModuleBridgeKt.countFromJS(
            subscriptionId: subscriptionId,
            previous: previous,
            onSuccess: { result in
                resolve(result)
            },
            onError: { error in
                reject("RN_FEEDBACKMODULE_ERROR", error, nil)
            }
        )
    }

    /// Unsubscribe from a flow
    @objc(unsubscribeFromToolkitUseFlow:resolve:reject:)
    func unsubscribeFromToolkitUseFlow(_ subscriptionId: String,
                  resolve: @escaping RCTPromiseResolveBlock,
                  reject: @escaping RCTPromiseRejectBlock) {

        // Call Kotlin bridge function
        FeedbackModuleBridgeKt.unsubscribeFromToolkitUseFlowFromJS(
            subscriptionId: subscriptionId,
            onSuccess: { result in
                resolve(result)
            },
            onError: { error in
                reject("RN_FEEDBACKMODULE_ERROR", error, nil)
            }
        )
    }
}
//
//  RNModuleBridge.m
//
//  Generated by reakt-native-toolkit for React Native 0.83+ new architecture.
//  Objective-C bridge file to export Swift modules to React Native.
//  This uses RCT_EXTERN_MODULE and RCT_EXTERN_METHOD macros to register
//  methods with React Native's TurboModule interop layer.
//  Do not modify manually.
//

#import <React/RCTBridgeModule.h>

// NavigationModule - NavigationModule
@interface RCT_EXTERN_MODULE(NavigationModule, NSObject)

RCT_EXTERN_METHOD(goBack:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

@end

// FeedbackModule - FeedbackModule
@interface RCT_EXTERN_MODULE(FeedbackModule, NSObject)

RCT_EXTERN_METHOD(sendFeedback:(NSString *)param
                  resolve:(RCTPromiseResolveBlock)resolve
                  reject:(RCTPromiseRejectBlock)reject)

@end

Closing

We welcome any suggestions for modifications, improvements, or optimizations. I would also like to extend my sincerest gratitude to everyone here—it is your work that expands the possibilities for both React Native and Kotlin Multiplatform! 🚀

@DGuang21 DGuang21 closed this Jan 28, 2026
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

Successfully merging this pull request may close these issues.

1 participant