How to use Swift in your React Native app

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!

59