React Native doesn't pick up native iOS module - objective-c

I'm trying to set up a native iOS module for React Native with Swift.
Here's my Swift file:
// SwiftManager.swift
import Foundation
#objc(SwitchManager)
class SwitchManager: NSObject {
#objc func addEvent(_ name: String) -> Void {
NSLog("It works!")
}
}
And here is my implementation file:
// SwiftManager.m
#import <React/RCTBridgeModule.h>
#interface RCT_EXTERN_MODULE(SwitchManager, NSObject)
RCT_EXTERN_METHOD(addEvent:(NSString *)name)
#end
Then, I try to use it in my React Native code like so:
// index.ios.js
import React, { Component } from 'react';
import { NativeModules } from 'react-native';
const SwitchManager = NativeModules.SwitchManager;
SwitchManager.addEvent('Birthday Party');
// ...
But SwitchManager ends up being undefined. In another project I put together, I have successfully created a native module and I can't recall doing anything different. Do you have any ideas on why RN wouldn't pick up this native module?

I reverted all changes to a clean version of my repo and created the files from scratch again in Xcode. React Native is able to pick up the native module now. I think the problem could have been related to renaming things around and Xcode not keeping references properly, although I can't say for sure because I had several changes in my working tree.

Related

Where can I find my MainActivity.java when running a Android simulator

I just mad my first React native project and I'm trying to lock my screen orientation to landscape. I installed this package
https://github.com/yamill/react-native-orientation#configuration
with npm install react-native link react-native-orientation and now I need to
Implement onConfigurationChanged method in MainActivity.java
So this code
import android.content.Intent; // <--- import
import android.content.res.Configuration; // <--- import
public class MainActivity extends ReactActivity {
......
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Intent intent = new Intent("onConfigurationChanged");
intent.putExtra("newConfig", newConfig);
this.sendBroadcast(intent);
}
......
}
But where can I find that? From what I've seen is that you can find it in a android studio project. But I don't have a project, I just go to the AVD manager to run my virtual device. So where am I supposed to add this code?
The whole aim of expo is to handle everything related to android or ios for you.
Thus, you won't have access to these two folders unless you eject your app from expo as it's explained here on the official doc.
However, if your goal is to handle screen orientation, expo has a built-in API for you that you may find here.
Or even simpler, you could just update your app.json and add this line:
"expo": {
...
"orientation": "landscape"
...
}
Have a look at the official doc if you want to see everything that you can handle with this configuration file.

Can my react-native application use xcode swift libraries? and How?

I'm currently quite new to react-native and xcode. So basically, I made a program on xcode using swift and importing a library, but now I need to use this code in my react native application. I don't know if it is possible and how to go about doing it.
Unfortunately it really depends on how you want to use your swift code, because React Native provides different methods for different circumstances, but I will try to give you an example of an basic "Event Bridge".
First of all every React Native Application automatically generates an iOS and Android project. Navigate to the iOS folder and open the Xcode workspace. Afterwards just drag your swift source file into the project (like in every other native application).
As React Native uses an Objective-C Bridge we cannot directly use the Swift code so we need to bridge Swift -> Objc -> Javascript.
Lets first dive into your Swift class:
You need to be able to communicate with objc and export the methods to the react native runtime, here is an example of the swift file
// CustomObject.swift
import Foundation
#objc(CustomObject)
class CustomObject: NSObject {
#objc static var isSelected = false // our state
#objc
func selectObject() {
CustomObject.isSelected.toggle()
print(CustomObject.isSelected ? "selected" : "not selected")
}
#objc
func getState(_ callback: RCTResponseSenderBlock) {
callback([NSNull(), CustomObject.isSelected]) // this will return into a
// javascript callback to retrieve the current state
}
// this is needed to make sure that this will run on the main thread
#objc
static func requiresMainQueueSetup() -> Bool {
return true
}
}
Now we need to call these methods from Objc and expose it to JavaScript.
// CustomObject.m
#import "React/RCTBridgeModule.h"
#interface RCT_EXTERN_MODULE(CustomObject, NSObject)
RCT_EXTERN_METHOD(selectObject)
RCT_EXTERN_METHOD(getState: (RCTResponseSenderBlock)callback)
#end
This is all you have to do.
Now you can navigate back to your JavaScript Application and import NativeModules.
import React, {useState} from 'react';
import {View, NativeModules, Button, Text} from 'react-native';
const TestComponent = () => {
const [selected, setSelected] = useState(false);
const triggerSelection = () => {
NativeModules.CustomObject.selectObject()
NativeModules.CustomObject.getState( (err, isSelected) => { // Edited
setSelected(isSelected);
});
}
return (
<View>
<Button title="Button" onPress={triggerSelection} />
<Text>{selected}</Text>
</View>
);
}
To pass more complex structures from native to javascript, just take a look at this documentation, it is actually not that bad.
React Native - Native Modules

ReactNative TabBarIOS Undefined

Hell, I'm trying to create a tab bar in my react native app, but after importing it, it appears it's always undefined. Has this component been deprecated? I still see it listed in the docs. https://facebook.github.io/react-native/docs/tabbarios.html
import React, { Component } from 'react';
import {
TabBarIOS
} from 'react-native';
export default class App extends Component {
render() {
return (
<TabBarIOS/>
);
}
}
I'm using react-native 0.59.3
Looks like this has been removed as part of a core cleanup effort. There doesn't appear to be any native alternative that also behaves correctly on tvos.
https://github.com/facebook/react-native/commit/02697291ff41ddfac5b85d886e9cafa0261c8b98
I've gone ahead and extracted TabBarIOS out into a native module for anyone looking for this.
https://github.com/modavi/NativeIOS
install instructions:
npm install git+https://github.com/modavi/NativeIOS#master
react-native link native-ios

react-native : Native modules with same name for both android and iOS

I'm trying to build a react-native wrapper around our existing native android and ios SDK. I want to know if I can use the same class name and class methods for both the Android and iOS bridge modules? And how do I map the right module to be called for the right device?
For instance, for iOS :
// CalendarManager.h
#import "RCTBridgeModule.h"
#interface CalendarManager : NSObject <RCTBridgeModule>
#end
// CalendarManager.m
#implementation CalendarManager
RCT_EXPORT_MODULE();
#end
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
//Some work
}
For Android :
public class CalendarManager extends ReactContextBaseJavaModule {
public CalendarManager(ReactApplicationContext reactContext) {
super(reactContext);
}
#Override
public String getName() {
return "CalendarManager";
}
#ReactMethod
public void addEvent(String name, String location) {
//Some work
}
}
And in my js file, if I have something like this :
import { NativeModules } from 'react-native';
NativeModules.CalendarManager.addEvent("Name", "Location")
Which one will be executed? Is there a way where I could route it to the right function based on the device?
Thanks.
You don't have to do that routing yourself. That is build into React-Native. It knows which type of device you're on.
React Native packages a different version of the code that you have in your project according to the target platform that you set when you create the application APK or IPA. That is, when you do react native run-android for instance, it will compile the native Java code present in the Android project located in the android folder and pack the APK with that. Then JavaScript will have access to the native Java code. Same thing for iOS with the Objective-C/Swift code. So yes, you can (actually, I'd say you must) use the same name for the native modules.

Cross-platform React Native file structure

I'm coming from Ionic 2 where I write my code once, and it runs everywhere.
According to this page https://facebook.github.io/react-native/docs/platform-specific-code.html "When building a cross-platform app, you'll want to re-use as much code as possible."
How can I "reuse" code across iOS and Android in ReactNative? In the starter app I see two files: index.ios.js and index.android.js. Is there something like index.js that I can use for sharing across platforms?
It seems like there should be one file for shareable code, since that web page mentioned above also shows how you can use the Platform module to check what OS you are on.
I realize that sometimes you need different behavior on different platforms, but, assuming that my app is simple enough, and my code ends up exactly the same on both platforms, do I have to copy/paste from one to the other? How can I pull same-functionality into it’s own file to be shared on both platforms?
What am I missing?
Thanks!
Here's what you can do. Create a file say main.js that will act as your root page.
main.js
import React, { Component } from 'react';
import { Text } from 'react-native';
export default class Main extends Component {
render() {
return (
<Text>Hello world! from both IOS and Android</Text>
);
}
}
Now on your index.android.js and index.ios.js import that main file and register your Main component as root using AppRegistry.
index.android.js and index.ios.js
import React from 'react';
import {
AppRegistry
} from 'react-native';
import Main from './main';
AppRegistry.registerComponent('DemoApp', () => Main);
if you run react-native run-android and react-native run-ios the Main component will be rendered for both the platforms.
All of the Components with No IOS and Android suffix works for both platforms. Like Navigator works for both ios and android but NavigatorIOS works only for ios. So you'll have to make sure that you're using the cross platform components.
If you need to use platform specific components, then you can use platform detection login to do so:
import { Platform } from 'react-native';
if(Platform.OS === 'ios').....
or
if(Platform.OS === 'android')...