How to Prevent Users From Taking Screenshots in React-Native Apps
A simple example of a work-around is to take a photo of the device with another one.
Anyway, is it even possible?
Android
It's quite simple to efficiently prevent screenshot-taking in Android.
There are two approaches you might take. The first one is to forbid screenshot taking in the whole application (easier to implement) and the second one is to handle the forbid and allow methods from JavaScript code (this approach requires you to implement a bridge from Android to JavaScript code).
Forbid screenshot taking in whole application
Add the code below with + signs to the MainActivity.java file located here: android/app/src/main/java/com/your-app name/MainActivity.java
Note: This is a full file example (RN 0.61.5 version). If your project already contains this file, you can copy its entire content and replace your-app-name
with your application's real name. You can find this at the top in the MainApplication.java
or MainActivity.java
file. Don't forget to remove the + signs!
package com.your-app-name;
import com.facebook.react.ReactActivity;
+ import android.view.WindowManager;
+ import android.os.Bundle;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "your-app-name";
}
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().setFlags(
+ WindowManager.LayoutParams.FLAG_SECURE,
+ WindowManager.LayoutParams.FLAG_SECURE
+ );
+ }
}
That's it. Screenshot-taking is locked now in whole application.
Forbid and allow screenshot-taking using JavaScript code
Firstly, create a new file under this path: android/app/src/main/java/com/your-app-name/PreventScreenshotModule.java
I named my module PreventScreenshotModule.java
but, of course, module's name is up to you
Note: Change your-app-name
to your application's real name. Also make sure to rename the "PreventScreenshotModule" fields in this code in case you chose a different name. Feel free to change the Promises output messages as well
Add the following code
package com.your-app-name;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
import android.view.WindowManager;
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
public class PreventScreenshotModule extends ReactContextBaseJavaModule {
private static final String PREVENT_SCREENSHOT_ERROR_CODE = "PREVENT_SCREENSHOT_ERROR_CODE";
private final ReactApplicationContext reactContext;
PreventScreenshotModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
@Override
public String getName() {
return "PreventScreenshotModule";
}
@ReactMethod
public void forbid(Promise promise) {
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
getCurrentActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
promise.resolve("Done. Screenshot taking locked.");
} catch(Exception e) {
promise.reject(PREVENT_SCREENSHOT_ERROR_CODE, "Forbid screenshot taking failure.");
}
}
});
}
@ReactMethod
public void allow(Promise promise) {
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
getCurrentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
promise.resolve("Done. Screenshot taking unlocked.");
} catch (Exception e) {
promise.reject(PREVENT_SCREENSHOT_ERROR_CODE, "Allow screenshot taking failure.");
}
}
});
}
}
Create another file (again, the name is up to you) under the same path: android/app/src/main/java/com/your-app-name/CustomPreventScreenshotPackage.java
Note: Change your-app-name
to your application's real name. Also make sure to edit all the "CustomPreventScreenshotPackage" fields in this code in case you chose a different package name
Note: Paste your module's name if you changed it from "PreventScreenshotModule"
package com.your-app-name;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CustomPreventScreenshotPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(new PreventScreenshotModule(reactContext));
}
}
Open the MainApplication.java file located under this path android/app/src/main/java/com/your-app-name/MainApplication.java
Add import on top of the file and add the package to the getPackages()
method in the MainApplication class, follow the + signs in the code below.
Note: Change the package's name if you picked a different name (in both the import section and the actual code).
+ import com.prevent_screenshot.CustomPreventScreenshotPackage;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
...
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
+ packages.add(new CustomPreventScreenshotPackage());
return packages;
}
...
Now you can access the allow and forbid methods inside of the JavaScript code; the module's name depends on your name, in this case it's PreventScreenshotModule
from NativeModules
Check out this example
import { NativeModules } from 'react-native';
const forbidFunction = async () => {
try {
const result = await NativeModules.PreventScreenshotModule.forbid();
console.log(result);
} catch (e) {
console.log(e);
}
}
const allowFunction = async () => {
try {
const result = await NativeModules.PreventScreenshotModule.allow();
console.log(result);
} catch (e) {
console.log(e);
}
}
iOS
Unfortunately, it's almost impossible to prevent screenshot-taking on iOS devices. There is no simple or efficient solution. You can only get information AFTER taking a screenshot and display some information or pop-up to the user.
Historical note: Famous application Snapchat had this feature in the past. They used some kind of trick that relied on forcing users to constantly be touching the screen to open messages. This meant that if a user tried to take a screenshot, the system action that handles this would force them to stop touching the screen for a moment. As a result, the message would close and the resulting screenshot would show a different screen. Still, this solution is far from perfect and may not always work. Moreover, iOS made implementing this workaround impossible a few releases ago.
Current tricks and solutions: There are basically two ways to prevent users from taking screenshot. Neither is good in terms of performance and battery drain.
The first one is to create an invisible 60 fps animation on top of the "secured" screen. Because the iOS screenshot feature only saves a single frame, it would only capture the overlaid animation, but no the content you wish to hide.
The second one is paying for ScreenShieldKit, which is probably a variation of the solution described above.