How to Close A UIViewController From Your React Native App
How to run dismissViewController from the JavaScript side

Recently, I was working on the Socket Mobile React Native CaptureSDK, trying to add support for the new SocketCamC860 on iOS.
In the React Native SDK, Both SocketCamC820 and SocketCamC860 are implemented via the respective native SocketCam view controllers from the iOS and Android SDKs. To utilize the iOS native SocketCam, I use ObjectiveC for the native modules. Fore Android, I am using Java.
In this post I’ll go over how I was able to implement a means from the JavaScript portion of the React Native to “close” the SocketCam window that was delivered to the React Native UI via iOS.
To do this, I first needed to incorporate native modules.
Native Modules
Sometimes a React Native app needs to access a native platform API that is not available by default in JavaScript, for example the native APIs to access Apple or Google Pay. The NativeModule system exposes instances of Java/Objective-C/C++ (native) classes to JavaScript (JS) as JS objects, thereby allowing you to execute arbitrary native code from within JS.
Think of my scenario.
I need to be able to initiate (or open) our SocketCam window in a React Native app. Since we have a means of using these windows in our iOS and Android apps already, it makes sense to implement the SocketCam window already provided by the respective native sdks.
This means I need to communicate from the React Native UI, to the iOS SDK, grab the SocketCam viewController, and pull back into the React Native UI so it can be used in the context of the React Native app.
I won’t go into details on how I was able to get all of that communication to work as it’s rather complex. I also won’t go into detail about how this works on the Android side of things as it’s not related to this blog topic.
What you should take away is that in order to achieve this, I needed to make use of the NativeModule system provided by React Native.
Below is how you can reference the NativeModule in a React Native app.
import React, {useState, useEffect, useRef} from 'react';
import { NativeModules } from 'react-native';
const {OurNativeModule} = NativeModules;
const App = () =>{
useEffect(()=>{
OurNativeModule.ourNativeModuleFunction();
})
}In your React Native app’s ios project folder, you will need two files to use ObjectiveC in this project that will represent your iOS native module. We will creat OurNativeModule.h and OurNativeModule.m.
The .h file is the header file, it is the declaration of the .m file. Together these files make up a class declaration.
Below is what our header file would look like.
// OurNativeModule.h
#import <React/RCTBridgeModule.h>
@interface OurNativeModule : NSObject <RCTBridgeModule>
@endAnd below is what our .m file (or our “implementation” file).
// OurNativeModule.m
#import "OurNativeModule.h"
@implementation OurNativeModule
RCT_EXPORT_MODULE(OurNativeModule);
@end For more on using Native Modules in React Native for iOS, you can check out these docs.
Now that we have this setup, let’s dive in to how to declare a function in the native module, and how to call it from the React Native side.
Our Function
First, implementing functions that can be used by our React Native app in ObjectiveC side of our code is rather simple. All you need to do is make use of RCT_EXPORT_METHOD. See below.
// OurNativeModule.m
#import "OurNativeModule.h"
@implementation OurNativeModule
RCT_EXPORT_MODULE(OurNativeModule);
RCT_EXPORT_METHOD(ourNativeModuleFunction)
{
// YOUR CODE HERE
}
@endHere we are exporting ourNativeModuleFunction as a React usable function so log as the React Native App has a connection to the native modules. This connection can be used by accessing it NativeModules.OurNativeModule.
Now that we have that settled, let’s figure out how SocketCam’s window is created and used in the iOS SDK.
UIViewController
In the case of SocketCam, the iOS SDK makes use of a subclass of something called UIViewController, which is a class provided by the Apple UIKit.
A view controller’s main responsibilities include the following:
- Updating the contents of the views, usually in response to changes to the underlying data
- Responding to user interactions with views
- Resizing views and managing the layout of the overall interface
- Coordinating with other objects — including other view controllers — in your app
It’s also particularly useful for accessing the device’s camera for data capture purposes.
So our next question is, how can we “grab” the UIViewController?
“Grabbing” the UIViewController
There are a few ways to do this, but the way we’re going to “grab” it is by root view controller of the application. This one works for our case because there is really only one UIViewController instance we’re going after — the one from the iOS SDK.
You can access the root view controller by making use of the UIApplication class and basically working our way down to rootViewController. See below.
UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;Now that we know how to gran the view, how can we “close” it?
“Closing” the UIViewController
Closing it is actually the simplest part of this process. We can do this by making use of Apple’s dismissViewControllerAnimated method. This method allows us to dismiss (i.e. “close”) a UIViewController subclass that was presented modally.
You can execute this on the rootViewController like so.
[rootViewController dismissViewControllerAnimated:YES completion:nil];Put it all together, and our method looks like this.
RCT_EXPORT_METHOD(dismissViewController) {
UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
[rootViewController dismissViewControllerAnimated:YES completion:nil];
}Now, to be safe you should wrap the body of the method in dispatch_async(dispatch_get_main_queue(), ^{…}). By wrapping it in this dispatcher, you ensure that you only fire this method asynchronously on the main thread.
RCT_EXPORT_METHOD(dismissViewController) {
dispatch_async(dispatch_get_main_queue(), ^{
UIViewController *rootViewController = UIApplication.sharedApplication.keyWindow.rootViewController;
[rootViewController dismissViewControllerAnimated:YES completion:nil];
});
}UI updates should always be done on the main thread. This way you prevent issues with UI updated from background threads.
Finally, in our React Native App, add the below line wherever you want in your React Native Code. Maybe you add it as a function for a button or some other event.
import React, {useState, useEffect, useRef} from 'react';
import { NativeModules } from 'react-native';
const {OurNativeModule} = NativeModules;
const App = () =>{
useEffect(()=>{
console.log("Hello World");
})
const closeUiViewControllerFromRNApp = () =>{
OurNativeModule.dismissViewController()
}
return (
<View>
<Pressable onPress={onPress}>
<Text>Close UIViewContrller</Text>
</Pressable>
</View>
)
}There you have it! By leveraging native modules to communicate between the React Native UI and the ObjectiveC code, and knowing how to effectively “grab” and “c̶l̶o̶s̶e̶ dismiss” the UIViewController, you can know close a native view from your React Native UI.
UPDATE!
I have since published a post that shows a more efficient way to accomplish this goal should you need one. You can read here!
Do you have another way to remove a UIViewController from React Native UI? Let me know in the comments!
You can also subscribe via email and get notified whenever I post something new!






