Exposing SwiftUI Views to React Native: An Integration Guide

Oskar Kwaśniewski
No items found.

In short

Reading this article, you'll learn how to seamlessly integrate SwiftUI views in React Native and enhance your app’s iOS capabilities with SwiftUI superpowers.

Since its release in 2019, SwiftUI's been shifting our approach to writing iOS apps from imperative to declarative, bringing us closer to React. Now, 6 years later, SwiftUI is used in many apps in production, with Apple increasingly releasing SwiftUI-exclusive APIs, underscoring its strategic direction.

In this blog post, we will explore how you can integrate SwiftUI views within your React Native components, demonstrating how to expose SubscriptionStoreView to the React Native app to create a neat paywall with just a few lines of SwiftUI code.

Note: This blog post assumes you have prior knowledge of SwiftUI and native development.

Setup for embedding SwiftUI views in React Native

To get started with embedding SwiftUI views in React Native, create a new library. The easiest way to do so is to use react-native-builder-bob:

In the library type, let’s choose Fabric View:

Choosing FabricView in library type

Next, navigate to the example folder, install pods, and launch the project in Xcode. After opening the project, navigate to Pods > Development Pods > SwiftUIStoreView in the sidebar.

As you can see, there are already three files created for us by Bob. The SwiftUIStoreViewViewManager.mm file is used by the old architecture, while SwiftUIStoreViewView.h and SwiftUIStoreViewView.mm are used by the new one.

Creating SwiftUI components

Let’s start by creating two new Swift files. The first one will be the SwiftUI implementation, which will know nothing about React Native. The second one will create a bridge between our native implementation and SwiftUI. Let’s call them SwiftUIStoreView.swift and SwiftUIStoreViewProvider.swift respectively.

After adding the files, remember to update the .podspec file to include .swift in our sources.

Following this change, reinstall Cocoapods. If you notice an error compiling about Swift not being able to build the Objective-C module “SwiftUIStoreView”, add #ifdef __cplusplus and #endif in the header file:

Next, let’s implement SwiftUIStoreView, which will use StoreKit’s built-in view, SubscriptionStoreView:

Let’s break down what’s happening in the code snippet above.

First, we created a new SwiftUI view as usual, checking if we are running iOS 17 because this is the version where this API has been introduced (normally, you should bump your development target or provide a fallback for older versions).

Then, we used ObservableObject to create a class called Props, which will be used by our provider class to bridge React Native and SwiftUI. Every time we change a property inside the ObservableObject, our view will re-render with new data (same as React!).

If you are targeting iOS 17+, there is a newer alternative available: Observable macro.

Connecting SwiftUI with React Native

To connect SwiftUI with React Native, let’s create the provider class (SwiftUIStoreProvider):

We’ve just created a new class that’s exposed to Objective-C using @objc annotation. Inside it, we create a new instance of the Props class that will be used to communicate with the SwiftUI view.

Then, we use the built-in UIHostingController that allows us to embed SwiftUI inside the UIKit view. The setup of creating a new UIHostingController , together with the SwiftUI view, can be found inside the setupView method. To embed a view controller inside another one, we have to use view controller containment, which can be achieved by a utility method from React Native called reactAddController(toClosestParent:).

We also use Auto Layout to pin the edges of the SwiftUI view to the edges of the provider view. The size of the provider view will be then dictated by React Native, specifically Yoga.

Here is how it looks on a diagram:

The structure of our SwiftUI React Native integration

Last but not least, we have to use the view inside our Objective-C file. As a first step, add the import of Swift classes. The compiler automatically generates <Module_Name>-Swift.h header files so we can easily interop between Swift and Objective-C.

Let’s add this import on the top of the file:

In the snippet above, we’ve checked whether we are running in the Frameworks mode (either static or dynamic), where imports are prefixed with the module name. Otherwise, we would use a normal import.

Next, in the @implementation block, let’s change UIView to our custom view:

And then, in the initializer, we create an instance of StoreViewProvider:

If you want your library to support New Architecture, use the above import and return a new instance of StoreViewProvider in the ViewManager file related to old architecture:

In React Native, make sure that your view takes the whole available space by assigning it the proper styles:

After compiling the app, you’ll see a spinner with a “Loading Subscription” label:

Loading subscription label

That’s because we haven’t specified StoreKit product IDs yet! To achieve this, we first have to allow users of our custom view to pass props from React. The TypeScript Codegen spec describes our component’s interface, which is later used to generate native interfaces.

Now, let’s add a new property in your spec:

Next, we need to reinstall pods, which will re-run React Native Codegen. After that, let’s navigate to the SwiftUIStoreViewView.mm file and change the updateProps function to look like this:

Now, we must compare old and new props and then convert C++ collection type std::vector<std::string> to Objective-C’s NSArray<NSString *>. To do so, we need to iterate over the vector and append new products to the NSArray.

Additionally, we have to add a new property accessible by Objective-C in our SwiftUIStoreViewProvider:

The productIDs property uses the didSet mechanism in Swift, which updates the productIDs property on the Props class created earlier. The update of the props object triggers a re-render on the SwiftUI side.

For old architecture, use RCT_EXPORT_VIEW_PROPERTY:

After passing some dummy productIDs on the React side, you should see SwiftUI showing an error screen indicating that subscription is not available because we didn’t pass proper product identifiers from StoreKit.

Subscription unavailable

Testing with StoreKit Configuration

Let’s create a StoreKit configuration file to test if your view works properly! In Xcode, create a new file from the template and select “StoreKit Configuration File”. After creating the file, add new subscriptions by clicking the “plus” button in the bottom left corner.

StoreKit Configuration File

If you want to learn more about StoreKit Configuration, check out this blog post.

One last thing to do before we can test this is to tell Xcode to use this StoreKit file. Click on your App Scheme > Edit Scheme:

App Scheme > Edit Scheme

In the Options tab, choose the recently created StoreKit configuration file:

recently created StoreKit configuration file

Next, update the productIDs in App.tsx:

Now, you can build the app showing a fully featured paywall screen—all with just a few lines of SwiftUI code!

Working paywall implemented with SwiftUI

Congratulations, you’ve just implemented a SwiftUI view that works on both React Native architectures! It’s impressive what SwiftUI can achieve. To play around with bridging SwiftUI and React Native, check out this repo.

Thanks for reading!

Latest update:
February 27, 2025

FAQ

No items found.
React Galaxy City
get in touch

This information will be used only to contact you. For details, check our Privacy Policy.

React Galaxy City
get in touch