This run starts from the feat/turbomodule-swift branch.
Start from there up to the [TurboModule] Test the swift Turbomodule
section. Then, follow the steps below to move your logic to a Swift implementation file.
This guide will teach how to implement a function call in the Native platform that produces a result in Javascript as an asynchronous event, instead of using a promise or a synchronous call. It also show how to set up properly the delegate pattern between Swift and Objective-C++, in case the Swift implementation needs some state.
- [Codegen] Update Codegen Specs
- [Swift] Implement the Swift logic
- [Obj-C++] Implement the Objective-C++ logic
- [Test] Test the new feature in your app
- Open the
calculator/src/NativeCalculator.js
file and add the following lines
export interface Spec extends TurboModule {
// your module methods go here, for example:
add(a: number, b: number): Promise<number>;
+ // eventful Sqrt
+ eventfulSqrt(a: number): void;
+ addListener: (eventType: string) => void;
+ removeListeners: (count: number) => void;
}
Note: The addListener
and removeListeners
implementations will be provided by React Native.
Note: This will be fixed in the stable release of 0.71
- Open the
calculator/ios/Calculator.swift
file - Add the following
CalculatorDelegate
protocol in it. This protocol will be implemented by the Objective-C++ class to provide the send event method to the Swift implementation
@objc protocol CalculatorDelegate {
func sendEvent(name: String, result: Double)
}
- In the Calculator class, add a property to store the delegate (remember the
weak
keyword):
@objc class Calculator: NSObject {
+ @objc weak var delegate: CalculatorDelegate? = nil
// rest of the class
}
- Transform the static method into instance method
@objc class Calculator: NSObject {
@objc weak var delegate: CalculatorDelegate? = nil
- @objc static func add(a: Int, b: Int) -> Int {
+ @objc func add(a: Int, b: Int) -> Int {
return a+b
}
}
- Add the new method implementation:
@objc class Calculator: NSObject {
@objc weak var delegate: CalculatorDelegate? = nil
@objc func add(a: Int, b: Int) -> Int {
return a+b
}
+ @objc func eventfulSqrt(value: Double) {
+ delegate?.sendEvent(name: Event.sqrt.rawValue, result: sqrt(value));
+ }
}
- Add an extension for the supporting logic:
extension Calculator {
// List of emittable events
enum Event: String, CaseIterable {
case sqrt
}
@objc
static var supportedEvents: [String] {
return Event.allCases.map(\.rawValue);
}
}
- Open the header file
calculator/ios/RNCalculator.h
and make it extendRCTEventEmitter
#import <Foundation/Foundation.h>
+ #import <React/RCTEventEmitter.h>
#if RCT_NEW_ARCH_ENABLED
#import <RNCalculatorSpec/RNCalculatorSpec.h>
-@interface RNCalculator: NSObject <NativeCalculatorSpec>
+@interface RNCalculator: RCTEventEmitter <NativeCalculatorSpec>
#else
#import <React/RCTBridgeModule.h>
-@interface RNCalculator : NSObject <RCTBridgeModule>
+@interface RNCalculator : RCTEventEmitter <RCTBridgeModule>
#endif
@end
- Open the implementation file
calculator/ios/RNCalculator.mm
and:- Make it conform to the protocol
// Options 2.A - Conform to the protocol @interface RNCalculator () <CalculatorDelegate> @end
- Add a property to store the Calculator:
@implementation RNCalculator { Calculator *calculator; }
- Implement the initializer, passing self as a delegate
- (instancetype)init { self = [super init]; if(self) { // Option 2.B - Instantiate the Calculator and set the delegate calculator = [Calculator new]; calculator.delegate = self; } return self; }
- Given that we implemented a custom init, we need to implement the
requiresMainQueueSetup
method:
+ (BOOL)requiresMainQueueSetup { return NO; }
- Implement the
RCTEventEmitter
required fields. Notice that we leverage the Swift implementation
- (NSArray<NSString *> *)supportedEvents { return [Calculator supportedEvents]; }
- Implement the
CalculatorDelegate
requirements. Here we emit the event to the JS side of React Native. ThesendEventWithName:body
is provided by React Native itself.
- (void)sendEventWithName:(NSString * _Nonnull)name result:(double)result { [self sendEventWithName:name body:@(result)]; }
- Implement the Specs by calling the Swift code:
RCT_EXPORT_METHOD(eventfulSqrt:(double)a) { [calculator eventfulSqrtWithValue:a]; }
- from the
NewArchitecture
root app, run:
cd add ../calculator
cd ios
RCT_NEW_ARCH_ENABLED=1 bundle exec pod install
cd ..
- Open the
NewArchitecture/App.tsx
file and, inside thefunction App()
body:- Add the following line to handle the new state
const [sqrtState, setSqrtState] = useState<number | undefined>(undefined);
- Add a function to issue the computation:
async function computeSqrt() { RNCalculator.eventfulSqrt(4); }
- Register to the event emitter:
const eventEmitter = new NativeEventEmitter(RNCalculator); let subscription = null subscription = eventEmitter.addListener("sqrt", (event) => { setSqrtState(event); });
- Add the UI to test your code:
<Text>eventfulSqrt(4)={sqrtState ? `${sqrtState}` : '???' } </Text> <Button title='Compute' onPress={computeSqrt}/>
- Run
yarn ios
from theNewArchitecture
folder
Note: Not to pollute the repository, the code of the NewArchitecture
app has not been pushed. Follow the previous guides to set it up: