In this post, we will integrate a React Native app with share on social media feature on iOS using Swift.
Introduction
If you are not familiar with Native Modules on React Native - and it's ok because this tutorial is for you too! - know that it's most common to integrate a React Native with Objective-C (iOS) and Java (Android), but recently this have changed. With the grew of Swift language, have been popular create Native Modules with Swift for React Native apps on iOS. We still need to use some Objective-C to be able to use Swift, but this thing maybe can change soon.
To follow this tutorial, you will need to have a React Native app, and if you don't have yet, you can start a new one using npx react-native init
.
Currently, is not possible to integrate native code with Expo Managed Workflow, but you can use Expo CLI with Bare Worflow if you prefer.
Why Swift Code
Some features available to use on user phones only can be accessed with native code like Camera, Geolocation, Bluetooth, and more. For iOS, you can choose between Swift and Objective-C to implement these features on your app. Mostly React Native code is written with Objective-C because React Native core uses Yoga lib for layout that works better bridging with Objective-C because is written in C++. But how we can see on documentation we also can bridge our application with Swift to use these functionalities. In this article, we will see how to use Swift to integrate our app with Share with social medias
native popover.
Creating native method with Swift
The first step, is create the function that you need on native part like an implementation on native apps. We will create a File called RNShare.swift
on ios
folder from our React Native app. We use RN
before module name to indentify that is a Native Module to use on Javascript later.
In RNShare.swift
, we need to create class object.
import Foundation
class RNShare : NSObject {
}
Is important to import Foundation
on every Swift file in your modules
RNShare.swift
will have our all functions that we need to expose to Javascript related to this module.
Let's create a function called _open
that open the Share window on our app.
import Foundation
class RNShare : NSObject {
func _open() -> Void {
let controller = RCTPresentedViewController();
let shareController = UIActivityViewController(activityItems: ["Hello React Native"], applicationActivities: nil);
shareController.popoverPresentationController?.sourceView = controller?.view;
controller?.present(shareController, animated: true, completion: nil)
}
}
This function will open a window to share the message "Hello React Native" with the user's installed social medias. We are simply instancing our window with UIActivityViewController
, and adding this controller to our React Native view controller (RCTPresentedViewController
) and calling method present
to present on the screen.
Our goal is to recieve a dynamic message from Javascript and sends this message to social medias. Then, we can add some logic to recieve params with open
function.
import Foundation
class RNShare : NSObject {
func _open(options: NSDictionary) -> Void {
var items = [String]()
let message = RCTConvert.nsString(options["message"])
if message != "" {
items.append(message!)
}
if items.count == 0 {
print("No `message` to share!")
return
}
let controller = RCTPresentedViewController();
let shareController = UIActivityViewController(activityItems: items, applicationActivities: nil);
shareController.popoverPresentationController?.sourceView = controller?.view;
controller?.present(shareController, animated: true, completion: nil)
}
}
Now, we are recieving an options
object, with an object that our message will be passed (or whatever parameter you want).
Function open
was not created with an _
by coincidence. This function is private because we will only call it on Swift object. We need to create another public function that will indicate to call this function on main thread with some extra configs.
Also, we need to create a requiresMainQueueSetup
to manage our queue on main thread.
import Foundation
@objc(RNShare)
class RNShare : NSObject {
@objc static func requiresMainQueueSetup() -> Bool {
return false
}
// Reference to use main thread
@objc func open(_ options: NSDictionary) -> Void {
DispatchQueue.main.async {
self._open(options: options)
}
}
func _open(options: NSDictionary) -> Void {
var items = [String]()
let message = RCTConvert.nsString(options["message"])
if message != "" {
items.append(message!)
}
if items.count == 0 {
print("No `message` to share!")
return
}
let controller = RCTPresentedViewController();
let shareController = UIActivityViewController(activityItems: items, applicationActivities: nil);
shareController.popoverPresentationController?.sourceView = controller?.view;
controller?.present(shareController, animated: true, completion: nil)
}
}
Note that we add @objc
for open
, requiresMainQueueSetup
, and RNShare
. This means that this code will be called on Objective-C and you need this declarations to work as expected.
Creating bridge between Objective-C and Swift code
The Objective-C code is the code that able to expose native functions to React Native(Javascript). We need to create a file called RNShare.m
that will refer to our Swift functions and will expose it to our bridge with Javascript.
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(RNShare, NSObject)
RCT_EXTERN_METHOD(open:(NSDictionary *)options)
@end
Our class and method are now exposed to Javascript bridge.
Calling native module with Javascript
To be able to use native methods, we need to use NativeModules
from react-native. I like to create a folder called native
on source code and every file is a different native module.
RNShare.js:
import { NativeModules } from 'react-native';
const { RNShare } = NativeModules;
export default RNShare;
After expose native method, let's call our function in a React Native component.
App.js
import {Button, SafeAreaView} from 'react-native';
import RNShare from './native/RNShare';
const App = () => {
return (
<SafeAreaView style={{ flex: 1 }}>
<Button
title="Share"
onPress={() => RNShare.open({message: 'Bridge with Swift Dev.to Tutorial'})}
/>
</SafeAreaView>
);
};
Result
That's It! 🎉
Following this guide, you are able to create whatever methods you want in Swift to expose in your React Native apps. If you want to see more about native module integrations, check awesome-native-modules repository on github to see some native integrations with iOS and Android!
Top comments (3)
Don't forget to add this to the bridge file:
When you first create a swift file, you will be prompted to create a bridge. Create it and add this code to it.
Nice content, keep doing it
Thank you for the post! Is there any difference between using
RCT_EXTERN_MODULE
,RCT_EXTERN_METHOD
vsRCT_EXPORT_MODULE
,RCT_EXPORT_METHOD
from React Native docs?