Peakiq Blog
17 December 2025

We'll also cover the following:
error, cancelled, completed, and progress to provide real-time feedback to users.react-native-progress library.package.json
{
"dependencies": {
"react-native-notifications": "^5.1.0",
"react-native-background-upload": "github:memanoj/react-native-background-upload",
"react-native-background-upload-original": "npm:react-native-background-upload"
}
}
App.ts
import React, { useState, useEffect } from 'react';
import { Layout, Text, normalize } from 'component';
import { CustomThemeProps } from 'config/theme';
import { useThemeToggle } from 'hooks';
import RNBackgroundUploadiOS from 'react-native-background-upload';
import RNBackgroundUploadAndorid from 'react-native-background-upload-original';
import styled from 'styled-components/native';
import I18n from 'i18';
import RNFS from 'react-native-fs';
import base64 from 'base-64';
import utf8 from 'utf8';
import { Platform, View } from 'react-native';
import * as Progress from 'react-native-progress';
import { Notifications } from 'react-native-notifications';
const uploadUrl = 'https://api.escuelajs.co/api/v1/files/upload';
const ToggleButton = styled.TouchableOpacity`
background-color: ${({ theme }: CustomThemeProps) => theme.BUTTON_BG_COLOR};
`;
const ToggleButtonText = styled(Text)<{ FontSize: Number }>`
color: ${({ theme }: CustomThemeProps) => theme.BUTTON_TEXT_COLOR};
font-size: ${(prop: { FontSize: number }) => `${normalize(prop.FontSize)}px`};
`;
const TextDesc = styled(Text)<{ color?: string }>`
margin: ${normalize(2)}px;
color: ${({ theme, color }: CustomThemeProps & { color?: string }) =>
color ? color : theme.BUTTON_TEXT_COLOR};
padding: 12px;
font-weight: bold;
background-color: ${({ theme }: CustomThemeProps) => theme.BUTTON_BG_COLOR};
margin: 10px;
`;
const BackGroundLoader =
Platform.OS === 'ios' ? RNBackgroundUploadiOS : RNBackgroundUploadAndorid;
const Login: React.FC = () => {
const { toggleTheme, ThemeName } = useThemeToggle();
const [ProgressLoad, SetProgressLoad] = useState<number>(0);
useEffect(() => {
// Request notification permissions for iOS
Notifications.registerRemoteNotifications();
// Listen for notifications
}, []);
const resolveUpload = function (uploadId: any) {
return new Promise((resolve, reject) => {
let l1 = BackGroundLoader.addListener('error', uploadId, (data: any) => {
reject(data);
cleanup();
});
let l2 = BackGroundLoader.addListener(
'cancelled',
uploadId,
(data: any) => {
reject(data);
cleanup();
},
);
let l3 = BackGroundLoader.addListener(
'completed',
uploadId,
(data: unknown) => {
console.info('done');
resolve(data);
cleanup();
},
);
let l4 = BackGroundLoader.addListener(
'progress',
uploadId,
(data: any) => {
console.info(data.progress, typeof data.progress);
SetProgressLoad(data.progress / 100);
},
);
let cleanup = () => {
l1.remove();
l2.remove();
l3.remove();
l4.remove();
};
});
};
const sendLocalNotification = () => {
const fireDate = new Date(Date.now() + 1 * 1000); // 1 seconds from now
let localNotification = Notifications.postLocalNotification({
body: 'File Uploading!',
title: 'Your File Uploaded successfully done...',
sound: 'chime.aiff',
silent: false,
category: 'SOME_CATEGORY',
userInfo: {},
fireDate: fireDate.toISOString(), // Converting to ISO string format
});
console.info({ localNotification });
};
const CallUpload = async () => {
try {
let uri = RNFS.DocumentDirectoryPath + '/audio.m4a';
const sizeInBytes = 5 * 1024 * 1024; // 2 MB in bytes
const dummyString = 'A'.repeat(sizeInBytes);
const bytes = utf8.encode(dummyString);
const base64String = base64.encode(bytes);
await RNFS.writeFile(uri, base64String, 'base64');
if (Platform.OS === 'ios') {
uri = `file://${uri}`;
}
StartUpload(uri);
} catch (error) {
console.info(error);
}
};
const StartUpload = async (uri: string) => {
try {
const headers = {
'Content-Type': 'multipart/form-data',
Authorization: 'Bearer token',
};
const formData = {
file: JSON.stringify({ uri, name: 'test.m4a', type: 'audio/m4a' }), // no need to stringify for ios
patientId: '3265fdf1-9448-45dd-9ed4-85a1c2b36e92',
// others params also in string form
};
const options = {
url: uploadUrl,
path: uri,
method: 'POST',
field: 'file', // filed name
headers: headers,
type: 'multipart',
parameters: formData,
customUploadId: `u-${new Date().getTime()}`,
};
try {
console.info({ BackGroundLoader });
await BackGroundLoader.startUpload(options);
let res: any = await resolveUpload(options.customUploadId);
if (res?.responseBody) {
console.info(JSON.parse(res?.responseBody));
} else {
console.info(res);
}
if (Platform.OS === 'ios') {
await BackGroundLoader?.canSuspendIfBackground();
sendLocalNotification();
}
SetProgressLoad(0);
} catch (err) {
throw {
status: -1,
_error: err,
data: {
code: null,
detail: null,
message: 'Network error',
},
};
}
} catch (error) {
console.info(error);
}
};
return (
<Layout style={{ flex: 1 }}>
<ToggleButton onPress={toggleTheme}>
<ToggleButtonText FontSize={18}>
{I18n.t('THEMEIS')} : {ThemeName}
</ToggleButtonText>
</ToggleButton>
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{ProgressLoad !== 0 && (
<>
<Text>{uploadUrl}</Text>
<Progress.Bar progress={ProgressLoad} width={350} />
</>
)}
<TextDesc onPress={CallUpload} FontSize={18}>
upload
</TextDesc>
</View>
</Layout>
);
};
export default Login;
For Patch if needed
npx patch-package react-native-background-upload-original
By default, iOS does not wake up your app when uploads are done while your app is not in the foreground. To receive the upload events (error, completed...) while your app is in the background, add the following to your AppDelegate.mm:
#import "RNSplashScreen.h"
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <VydiaRNFileUploader.h> // import this
#import "RNNotifications.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.moduleName = @"Imanoj";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
[super application:application didFinishLaunchingWithOptions:launchOptions];
[RNSplashScreen show];
// Add RNNotifications monitoring
[RNNotifications startMonitorNotifications];
return true;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
return [self bundleURL];
}
- (NSURL *)bundleURL
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
//this is code form documentation that we have to added
- (void)application:(UIApplication *)application
handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)(void))completionHandler {
[[VydiaRNFileUploader sharedInstance] setBackgroundSessionCompletionHandler:completionHandler];
}
@end
Note : If you getting issue with android kotlin then you can apply the code and patch it
diff --git a/node_modules/react-native-background-upload-original/android/build.gradle b/node_modules/react-native-background-upload-original/android/build.gradle
index fa6963f..4d243c3 100755
--- a/node_modules/react-native-background-upload-original/android/build.gradle
+++ b/node_modules/react-native-background-upload-original/android/build.gradle
@@ -20,7 +20,7 @@ buildscript {
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-parcelize'