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

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

Related

Launch url in default video player on android with react native

I found this native android code for what I am trying to achieve
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(newVideoPath), "video/mp4");
startActivity(intent);
(Android intent for playing video?)
But I can't figure out how to use apply it in react native with the Linking.sendIntent api, or if that api is even capable of doing it.
I also tried this module, but it failed to build the project with the error method does not override or implement a method from a supertype
I don't want to write a native module for this.
Turns out you can't send data with an intent with the built in sendIntent api, however there's a handy library that's capable of doing that (react-native-send-intent).
So now I am able to achieve what I wanted like so:
import { Linking, Platform } from "react-native";
import SendIntentAndroid from "react-native-send-intent";
export function playVideo(url){
var fn = Platform.select({
android(){
SendIntentAndroid.openAppWithData(
/* "org.videolan.vlc" */null,
"https://www.w3schools.com/html/mov_bbb.mp4",
"video/*"
).then(wasOpened => {});
},
default(){
Linking.openURL(url).catch(err => {});
}
});
fn();
}
Despite the library not offering a function for launching the default app for a url, you can achieve it by passing in null as the packagename, since the function it uses under the hood Intent.setPackage(String packageName), accepts null as a value.

How do I render a Shoutem extension

I was wondering how I would render some Shoutem extension, for simplicity I am going to render it as my only component like so:
import 'es6-symbol/implement';
import React from 'react';
import {
AppRegistry,
View
} from 'react-native';
import { AppBuilder } from '#shoutem/core';
import { NavigationBar } from '#shoutem/ui';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import extensions from './extensions.js';
import { screens } from './extensions/kevinyclu.restaurants/app/index';
const List = screens.List;
const store = createStore((state, action) => state);
const App = () => <Provider store={store}><View><List /></View></ Provider>;
// noinspection JSCheckFunctionSignatures
AppRegistry.registerComponent('Restaurant', () => App);
But this gives me an error that says:
Though if I replace the const App = ... with the code that was initially there when I did shoutem configure
const App = new AppBuilder()
.setExtensions(extensions)
.setRenderNavigationBar(renderNavigationBar)
.build();
Then everything works fine, so I was wondering how would I use a Shoutem extension? Or am I missing the point of the extension completely?
You simply add it in the Builder by adding a screen. The flow is explained in our getting started docs. You create an extension, create a screen with a shortcut and then upload it to the Shoutem servers and install it in one of your apps on the Builder.
After that, you can go to the app in the Builder and add that new extension's screen by clicking the + button next to Screens. You can easily find your new extension by selecting the Custom category.
Remember that after installing a new app, you should run shoutem configure in the cloned app's directory. This will set up the new configuration you have after you've installed a new extension on the Builder.
Some advice; if you ever uninstall an extension on the Builder, it's good to re-clone your app completely, because shoutem configure will not remove the extension's from the directory, which may "hide" errors. For example, you could be importing something from that extension that you uninstalled, but you won't get an error because the files are all still there, even though they're uninstalled.

react native share in a single application

In my react-native app, I want to share a text message with a specific application, e.g whatsapp or texting application without having to first land on the dialog with all the social applications.
For instance if I press the share button and whatsapp is called directly.
I tried using react-native-share but it seems to not be working anymore.
You can use Linking, which gives you a general interface to interact with both incoming and outgoing app links.
For example:
import React, { Component } from 'react';
import { Linking, Button } from 'react-native';
export class App extends Component {
render() {
return <Button
onPress={() => {
let url = 'whatsapp://send?text=Hola Mundo';
Linking.openURL(url).then((data) => {
console.log('open whatsapp')
}).catch(() => {
console.log('App not installed')
});
}}
title="Whatsapp"
color="#4FBE3C"/>;
}
}
For Android, the React Native Share module uses the default ACTION_SEND android intent:
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
In order to have a different behavior, you need either write our own RN plugin that would talk to the app you want it to (if such feature is available) or find a similar plugin on npm.
I assume your plugin should do something like this:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
startActivity(sendIntent);
sendIntent.setPackage("com.whatsapp");

React Native doesn't pick up native iOS module

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.

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.