How to use Swift in your React Native app
Tags: reactnative, ios, swift
Published: 2021-07-06 12:19:33

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](https://twitter.com/tsapeta/status/1404552421528031234).

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](https://github.com/facebook/yoga) lib for layout that works better bridging with Objective-C because is written in C++. But how we can see on [documentation](https://reactnative.dev/docs/native-modules-ios#exporting-swift) 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.
```swift
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.
```swift
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.
```swift
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.
```swift
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.
```objc
#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:
```javascript
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
```jsx
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

![WhatsApp Image 2021-07-02 at 22.18.25_iphonexrspacegrey_portrait](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n0efyu3f4hucyb3rtivz.png)

## 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](https://github.com/akinncar/awesome-native-modules) on github to see some native integrations with iOS and Android!