I have some experience with React but I'm new to React Native.
I've played around for a while, but I got stuck when I tried to write a basic native module for iOS. I've tried with both Swift and Objective C. (I have some basic experience with Swift, but Objective C is completely new for me)
Some context:
The project is created with react-native-cli 2.0.1
I use XCode 9.1
Here is the .swift class
import Foundation
#objc(Something)
class Something: NSObject {
#objc
func printSomething() -> Void {
print("Something")
}
}
Here is the bridge file
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#interface RCT_EXTERN_MODULE(Something, NSObject)
RCT_EXTERN_METHOD(printSomething)
#end
And the Project-Brigding-Header.h
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import <React/RCTBridgeModule.h>
My App.js file
...
import { NativeModules } from 'react-native';
...
type Props = {};
export default class App extends Component<Props> {
componentDidMount() {
console.log('NativeModules: ', NativeModules);
}
...
}
Here is the problem. The output of console.log() says NativeModules is an empty object:
2018-02-22 18:19:04.590 [info][tid:com.facebook.react.JavaScript] 'NativeModules', {}
2018-02-22 18:19:04.589970+0200 velimo_app_rn[14748:400982] 'NativeModules', {}
I don't understand what I'm doing wrong. I've read pretty much everything I could find only related to the topic but I can't see what I do wrong.
If you have any suggestion, please let me know. Thanks!
The solution was to log the module name console.log(NativeModules.Something), not the whole NativeModules object. It wasn't anything wrong with my setup.
If you made changes to the native iOS code, then you need to rebuild the project by
rerunning npx react-native run-ios and restart the server npx react-native start.
I also relaunched the app in the simulator.
Related
I am trying to make a Swift Static library and apply it to Swift and Objective Project.
import Foundation
#objc open class Library001_Test: NSObject {
public override init(){}
#objc public func testPrint() {
print("My Name is Andi")
}
#objc public func getUUID(userName: String) -> String {
let uuid = UUID().uuidString
return "\(userName)'s UUID : \(uuid)"
}
}
I wrote the code like this using Swift.
And in the Edit Scheme menu, I changed the Build Configuration to Release and proceeded with Run. As a result, the 'libLibrary001.a' file and the 'Library001.swiftmodule' folder were created.
These two artifacts work well when pasted into a Swift project and imported.
But the problem is an Objective-C project.
I put both artifacts into my project and checked:
[General - Frameworks, Libraries. and Embedded Content] whether the library is recognized
Whether the library is recognized in [Build Phases - Link Binary With Libraries]
Check [Build Settings - Library Search Paths] address
Defines Module - Yes
And I put '#class Library001_Test;' in ViewController.h
#import <UIKit/UIKit.h>
#class Library001_Test;
#interface ViewController : UIViewController
#end
And in ViewController.m, '#import "ProductName-Swift.h" and the created Class were loaded.
#import "ViewController.h"
#import "SwiftInObjectiveC-Swift.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Library001_Test *test = [[Library001_Test alloc] init];
}
#end
error : Receiver 'Library001_Test' for class message is a forward declaration
error : Receiver type 'Library001_Test' for instance message is a forward declaration
An error occurred while doing this. I've tried all the methods I've found on the internet and I'm wondering where the problem is.
Is the code the problem? Did I not set it up well??
The Swift file created in the project is import well in Objective-C... Why the hell is the .a file not working like this?
My problem was with '(ProductName)-Swift.h'
If you look at how Swift Code is used in Objective-C, many articles say to import (ProductName)-Swift.h. So I only added the project header that I want to apply, but I also need to add the product header made from the library.
My problem was simple, but it took me a long time to figure it out. The error was not found 'Class' and 'func' in Swift static library. My workaround was resolved using the (LibraryProductName)-Swift.h of the library I created, rather than the (ProductName)-Swift.h of the project you are working on.
If you refer to the address below, you can prevent the error that occurred in advance.
https://medium.com/#mail2ashislaha/swift-objective-c-interoperability-static-libraries-modulemap-etc-39caa77ce1fc
Trying to learn React Native custom Native UI Components.
// FridgeCameraViewManager.swift
import UIKit
#objc(FridgeCameraViewManager)
class FridgeCameraViewManager: RCTViewManager {
override func view() -> UIView! {
let label = UILabel()
label.text = "Swift Component"
label.textAlignment = .center
return label
}
#objc static override func requiresMainQueueSetup() -> Bool {
return false
}
}
.
// FridgeCameraViewManager.h
#import <Foundation/Foundation.h>
#import "React/RCTViewManager.h"
#interface RCT_EXTERN_MODULE(FridgeCameraViewManager, RCTViewManager)
#end
.
// FridgeCameraView.js
import {requireNativeComponent} from 'react-native';
const FridgeCameraView = requireNativeComponent('FridgeCameraView', null);
export default FridgeCameraView;
When I try to use FridgeCameraView component somewhere in App.js, it works only If I build & run the project using Xcode. Otherwise, using hot reload when changing something, I get "Invariant Violation: tried to register two views with the same name FridgeCameraView".
Somehow, the error went away when I installed react-router-native. I think there was a package conflict or something that I was missing. Hopefully, this will be a fix for the ones who encounter this error in the future. I'm still waiting for explanations if somebody knows what's behind this weird error.
(Not a solution, but still want to contribute)
TL;DR:
Just press R in terminal to refresh app
Auto hot-reload will register FridgeCameraView twice, reason unknown
For those of you who are wondering what's going on, we're learning the React Native - iOS Native Component and following the tutorial:
Swift in React Native - The Ultimate Guide Part 1: Modules
Swift in React Native - The Ultimate Guide Part 2: UI Components
And this is part 2 UI Components that went wrong. As React / React Native are changing fast, this scared resource on native component soon became outdated.
I accidentally need to reinstall my macOS & node_module, no luck in getting rid of the error. Installing another package doesn't solve the issue either.
I suspect that during React Native bridging in Swift -> Objective C -> React-JS, React Native will register our customized module FridgeCameraView twice; but will need expect in Objective C to dig deeper on this issue. Any contribution is welcomed!
I am new to the development of React Native, I know iOS.
Just want to know about few points:
Can we add iOS (swift or objective C) code in a React Native App?
Can we add React Native Views into the existing iOS app?
I have googled and got the examples which point to that 2nd one is quite possible and Instagram did it in edit profile screen.
But I cannot find examples where it's a React Native app and we introduces swift or Objective-C code in between.
Please if anyone knows of any links or examples available for Case 1, please do let me know about it.
2nd question:
When we integrate react native with iOS, it will be through a bridge. Let's say I create an app in Swift, to get react native views in it, we have to go through 2 bridges, one converting Swift to Objective-C and then bridge between Objective-C to React Native. Is that correct? If yes will it be as fast as the native app?
Yes. You can do both of them
1.
https://facebook.github.io/react-native/docs/platform-specific-code.html
2.
https://codeburst.io/integrate-react-native-into-an-existing-app-ios-87c947a16044
https://facebook.github.io/react-native/docs/integration-with-existing-apps.html
Can we add iOS (swift or objective C) code in a React Native App
Yes, I was able to do so:-
I created a React Native project, compiled it and run it in the simulator.
Go to the iOS folder in directory structure in the project and open the .xcodeproj in xcode.
Create an objective c file (MyObjcClass) and make functions which you want to use in React.
MyObjcClass.h
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#interface MyObjcClass : NSObject <RCTBridgeModule>
#end
MyObjcClass.m
#import "MyObjcClass.h"
#implementation MyObjcClass
// tells react bridge to bridge our created class
RCT_EXPORT_MODULE()
- (NSDictionary *)constantsToExport {
return #{#"CreatedBy": #"Type any number and get Square"};
}
RCT_EXPORT_METHOD(squareNumber:(int)number getCallback:(RCTResponseSenderBlock)callback) {
callback(#[[NSNull null], [NSNumber numberWithInt:(number*number)]]);
}
Now we can call these methods in JS.
Below I will show how to call objective c in React.
App.js
import React from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
// to import native code
import {NativeModules} from 'react-native';
var MyObjcClass = NativeModules.MyObjcClass;
export default class App extends React.Component {
state = {
number:0
};
squareMe(num) {
if (num == '') {
return;
}
MyObjcClass.squareNumber(parseInt(num), (error, number) => {
if (error) {
console.error(error);
} else {
this.setState({number: number});
}
})
}
render() {
return (
<View style={styles.container}>
<Text style={styles.spaceBetween}>Objective C inclusion</Text>
<TextInput placeholder="type a number ...." style={styles.input} onChangeText={(text) => this.squareMe(text)}/>
<ListItem placeName={this.state.number}></ListItem>
</View>
);
}
}
yes we can do
import {Platform} from 'react-native';
const majorVersionIOS = parseInt(Platform.Version, 10);
if (majorVersionIOS <= 9) {
console.log('Work around a change in behavior');
}
To bridge an iOS native module into React Native, you can follow along the official instructions: iOS Native Modules.
To paraphrase the important parts:
Open your .xcworkspace file in your ios folder to open XCode
Select File > New > File
Select header file
Name the header and select your project and its tests as the targets
// RCTCalendarModule.h
#import <React/RCTBridgeModule.h>
#interface RCTCalendarModule : NSObject <RCTBridgeModule>
#end
Select File > New > File
Select Objective-C file. Name it the same as the header but with .m instead of .h. Select your project and its tests and targets
// RCTCalendarModule.m
#import "RCTCalendarModule.h"
#implementation RCTCalendarModule
// To export a module named RCTCalendarModule
RCT_EXPORT_MODULE();
#end
In your React Native App or component, import NativeModules and use your new method
import {NativeModules, Button} from 'react-native';
const {CalendarModule} = NativeModules;
const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
};
Note: You will need to keep rebuilding your ios app as you iterate (eg. run yarn ios after making a change to the .m or .h files)
For your second question, you can also add React Native into existing iOS apps. Follow these official instructions: Integration with Existing Apps.
I have been using React Native for a mobile app project for over a year now. I have a native component to bridge the BLE stack to the React Native portion of the app. Recently I upgraded to version 9.1 of XCode and I cannot get the React-Native Bridge to work within the iOS version. The RCTBridge is always nil so I can never use the eventDispatcher(). Here is my setup:
I have a native Swift component which I integrate into the app via a bridgin header. The origanization looks like this:
BLEScanner.swift (This is the native component)
BluetoothModuleBridge.m
Module-Practive-Bridging-Header.h (The bridging header)
Relevant code snippets from each file:
BLEScanner.swift
import Foundation
#objc(BLEScanner)
class BLEScanner: NSObject {
//....
var bridge: RCTBridge! // THIS IS ALWAYS NIL
//....
#objc func requestBluetoothState() -> Void {
print("REQUEST BLE STATE")
let ret = [
"enabled" : true
]
//THIS LINE WILL FAIL BECAUSE bridge IS NIL
self.bridge.eventDispatcher().sendDeviceEvent(withName: "BluetoothStateEvent", body: ret)
}
}
BluetoothModuleBridge.m
#import <React/RCTBridgeModule.h>
#interface RCT_EXTERN_MODULE(BLEScanner, NSObject)
RCT_EXTERN_METHOD(requestBluetoothState)
#end
Module-Practive-Bridging-Header.h
// BluetoothModule-Bridging-Header.h
#import <React/RCTBridge.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTRootView.h>
#import <React/RCTUtils.h>
#import <React/RCTConvert.h>
#import "AppDelegate.h"
#import "BugsnagReactNative/BugsnagReactNative.h"
#import "nokeLockSDK.h"
#import "nokeServerSDK.h"
#import "TI_aes_128.h"
What I have Tried
Updated React Native from 0.36 to 0.50.3
Tried running on multiple devices
Tried on older version of XCode
Tried compiling on different machine
Compared to a similar app that works and uses this same design.
I am very confused as to why the RCTBridge is returning as nil. It seems odd to me that an XCode update would cause this, however, it is the only change made.
Can anyone point me in the right direction in debugging this issue?
I am using XCode 9.1 and React Native 0.50.3
You do not have to subclass RCTEventEmitter. Just add the #objc attribute. So the variable should be #objc var bridge: RCTBridge!
Do you have your own initial method? If so try remove it. After my test, it has no problem.
And it is recommended to Subclass RCTEventEmitter instead.
If you upgraded to 9.1 and also React Native to 50.x from 30.x at the same time, you could have wrong imports, as that is something that changed around version 40.
It should be
#import <React/RCTBridgeModule.h>
If you don't want to subclass RCTEventEmitter, implement protocol RCTBridgeModule in your BLEScanner.
That will solve the problem.
I'm having issues linking libraries to a react-native project even after attempting to link manually multiple different times. I've gone through a ton of "praised" tutorials with no success.
I'm not an expert in Objective-C/iOS-native, so to isolate the issue I decided to create a HelloWorld Library and a HelloWorld react-native project.
Using:
react-native v0.41.2
react-native-cli v2.0.1
react-native-create-library v1.0.4
xcode v8.2.1
I did the following:
react-native-create-library Lib.
react-native init RNLibTest.
"Successfully linking" (Tried both manually and react-native link ...)
Here is the code:
RNLib
// ./ios/RNLib.h
#import <React/RCTBridgeModule.h>
#interface RNLib : NSObject <RCTBridgeModule>
#end
// ./ios/RNLib.m
#import "RNLib.h"
#implementation RNLib
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(helloWorld:(NSString *)world)
{
return [NSString stringWithFormat:#"hello %#", world];
}
#end
// ./index.js
import { NativeModules } from 'react-native';
const { RNLib } = NativeModules;
console.log(RNLib); // undefined
console.log(NativeModules); // Object: RNLib NOT included
export default RNLib;
HelloWorld / RNLibTest
// index.ios.js
import React, { Component } from 'react';
import { AppRegistry, Text, View } from 'react-native';
import RNLib from 'react-native-lib'; // I installed RNLib with npm and then "linked".
export default class RNLibTest extends Component {
componentDidMount() {
console.log(RNLib); // undefined
}
render() {
return (
<View>
<Text>Hello World</Text>
<Text>{
RNLib.helloWorld("world")
/* throws error "can't read property helloWorld of undefined" */
}</Text>
</View>
);
}
}
AppRegistry.registerComponent('RNLibTest', () => RNLibTest);
My questions:
If the code is not wrong, could it be the xcode project created by react-create-native-library somehow not working with newer versions of react-native?
Is anyone else having these issues? If so, what did you do to fix it?
If you haven't encountered this issue, are you using the latest version of react-native?
What version are you using of xcode, react-native-cli, etc...?
Did anyone have to downgrade to fix this issue?
UPDATE Feb/22/2017
There is a new release of react-native-create-library (v1.1.0) that supports v0.40+. I tried again and updated the code above, but I'm still seeing the same issue.
Here is a link to the GitHub issue. I also uploaded react-native-lib library and LibTest app to GitHub.
Not Sure Whether react-native-create-library support 0.41 RN But from the code i could say you need to import RCT*h files like <React/RCTBridgeModule.h>
Since this is single fine you can create a RNLib.h File in Xcode and check you would get compile time error
Thanks
I don't know what caused the issue but with react-native 0.42 out, I decided to try again.
Same code, same libraries, same everything BUT react-native 0.42, these were my results:
using react-native link => WORKED.
using manual installation => WORKED.
I'm not sure what happened in 0.41 but it is not happening in 0.42!