├── .github ├── ISSUE_TEMPLATE │ ├── 1-bug.yml │ └── 2-help-wanted.yml └── workflows │ └── stale.yaml ├── .gitignore ├── .npmignore ├── .yarnignore ├── CHANGELOG.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── RNBackgroundFetch.podspec ├── android ├── build.gradle ├── libs │ └── com │ │ └── transistorsoft │ │ └── tsbackgroundfetch │ │ ├── 1.0.4 │ │ ├── tsbackgroundfetch-1.0.4.aar │ │ ├── tsbackgroundfetch-1.0.4.aar.md5 │ │ ├── tsbackgroundfetch-1.0.4.aar.sha1 │ │ ├── tsbackgroundfetch-1.0.4.aar.sha256 │ │ ├── tsbackgroundfetch-1.0.4.aar.sha512 │ │ ├── tsbackgroundfetch-1.0.4.pom │ │ ├── tsbackgroundfetch-1.0.4.pom.md5 │ │ ├── tsbackgroundfetch-1.0.4.pom.sha1 │ │ ├── tsbackgroundfetch-1.0.4.pom.sha256 │ │ └── tsbackgroundfetch-1.0.4.pom.sha512 │ │ └── maven-metadata.xml ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── transistorsoft │ └── rnbackgroundfetch │ ├── HeadlessTask.java │ ├── RNBackgroundFetchModule.java │ └── RNBackgroundFetchPackage.java ├── app.plugin.js ├── docs ├── INSTALL-AUTO-ANDROID.md ├── INSTALL-AUTO-IOS.md ├── INSTALL-COCOAPODS-IOS.md ├── INSTALL-EXPO.md ├── INSTALL-LINK-ANDROID.md ├── INSTALL-LINK-IOS.md ├── INSTALL-MANUAL-ANDROID.md └── INSTALL-MANUAL-IOS.md ├── example ├── .buckconfig ├── .editorconfig ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .prettierrc.js ├── .ruby-version ├── .watchmanconfig ├── App.tsx ├── Gemfile ├── README.md ├── __tests__ │ └── App-test.tsx ├── android │ ├── app │ │ ├── _BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── fetchdemo │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── fetchdemo │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MainApplication.java │ │ │ │ └── newarchitecture │ │ │ │ ├── MainApplicationReactNativeHost.java │ │ │ │ ├── components │ │ │ │ └── MainComponentsRegistry.java │ │ │ │ └── modules │ │ │ │ └── MainApplicationTurboModuleManagerDelegate.java │ │ │ ├── jni │ │ │ ├── CMakeLists.txt │ │ │ ├── MainApplicationModuleProvider.cpp │ │ │ ├── MainApplicationModuleProvider.h │ │ │ ├── MainApplicationTurboModuleManagerDelegate.cpp │ │ │ ├── MainApplicationTurboModuleManagerDelegate.h │ │ │ ├── MainComponentsRegistry.cpp │ │ │ ├── MainComponentsRegistry.h │ │ │ └── OnLoad.cpp │ │ │ └── res │ │ │ ├── drawable │ │ │ └── rn_edit_text_material.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios │ ├── .xcode.env │ ├── FetchDemo.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── FetchDemo.xcscheme │ ├── FetchDemo.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── FetchDemo │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── main.m │ ├── FetchDemoTests │ │ ├── FetchDemoTests.m │ │ └── Info.plist │ ├── Podfile │ └── Podfile.lock ├── metro.config.js ├── package.json ├── scripts │ ├── simulate-fetch.sh │ └── watch ├── src │ └── Event.ts ├── tsconfig.json └── yarn.lock ├── expo └── plugin │ ├── src │ ├── androidPlugin.ts │ ├── iOSPlugin.ts │ └── index.ts │ └── tsconfig.json ├── index.d.ts ├── index.js ├── ios ├── RNBackgroundFetch.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ └── chris.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── RNBackgroundFetch │ ├── RNBackgroundFetch+AppDelegate.m │ ├── RNBackgroundFetch.h │ ├── RNBackgroundFetch.m │ └── TSBackgroundFetch.xcframework │ │ ├── Info.plist │ │ ├── _CodeSignature │ │ ├── CodeDirectory │ │ ├── CodeRequirements │ │ ├── CodeRequirements-1 │ │ ├── CodeResources │ │ └── CodeSignature │ │ ├── ios-arm64 │ │ └── TSBackgroundFetch.framework │ │ │ ├── Headers │ │ │ └── TSBackgroundFetch.h │ │ │ ├── Info.plist │ │ │ ├── Modules │ │ │ └── module.modulemap │ │ │ ├── PrivacyInfo.xcprivacy │ │ │ ├── TSBackgroundFetch │ │ │ └── _CodeSignature │ │ │ ├── CodeDirectory │ │ │ ├── CodeRequirements │ │ │ ├── CodeRequirements-1 │ │ │ ├── CodeResources │ │ │ └── CodeSignature │ │ ├── ios-arm64_x86_64-maccatalyst │ │ └── TSBackgroundFetch.framework │ │ │ ├── Headers │ │ │ └── TSBackgroundFetch.h │ │ │ ├── Modules │ │ │ └── module.modulemap │ │ │ ├── Resources │ │ │ ├── Info.plist │ │ │ └── PrivacyInfo.xcprivacy │ │ │ ├── TSBackgroundFetch │ │ │ └── _CodeSignature │ │ │ ├── CodeDirectory │ │ │ ├── CodeRequirements │ │ │ ├── CodeRequirements-1 │ │ │ ├── CodeResources │ │ │ └── CodeSignature │ │ └── ios-arm64_x86_64-simulator │ │ └── TSBackgroundFetch.framework │ │ ├── Headers │ │ └── TSBackgroundFetch.h │ │ ├── Info.plist │ │ ├── Modules │ │ └── module.modulemap │ │ ├── PrivacyInfo.xcprivacy │ │ ├── TSBackgroundFetch │ │ └── _CodeSignature │ │ ├── CodeDirectory │ │ ├── CodeRequirements │ │ ├── CodeRequirements-1 │ │ ├── CodeResources │ │ └── CodeSignature └── Resources │ └── PrivacyInfo.xcprivacy └── package.json /.github/ISSUE_TEMPLATE/1-bug.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report. 3 | title: "[Bug]: " 4 | labels: ["bug", "triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this support request. 10 | - type: checkboxes 11 | id: terms 12 | attributes: 13 | label: Required Reading 14 | description: "- I have read the [README](../blob/master/README.md) and [Debugging](../blob/master/README.md#debugging-1) section\n- I am aware of the [API Documentation](../blob/master/README.md#api-documentation)\n- I am aware of the [CHANGELOG](../blob/master/CHANGELOG.md)" 15 | options: 16 | - label: Confirmed 17 | required: true 18 | - type: input 19 | id: version 20 | attributes: 21 | label: Plugin Version 22 | description: What version of our software are you running? 23 | placeholder: "Consult your package.json (do not enter 'latest')" 24 | validations: 25 | required: true 26 | - type: checkboxes 27 | id: os 28 | attributes: 29 | label: Mobile operating-system(s) 30 | description: Which mobile operating-system(s) is this issue reported upon? 31 | options: 32 | - label: iOS 33 | - label: Android 34 | - type: input 35 | id: device-info 36 | attributes: 37 | label: Device Manufacturer(s) and Model(s) 38 | description: What is the device model(s) and manufacturer(s) 39 | placeholder: eg Google Pixel 6, iPhone 13 Pro 40 | validations: 41 | required: true 42 | - type: input 43 | id: device-os 44 | attributes: 45 | label: Device operating-systems(s) 46 | description: Affected device operating system 47 | placeholder: eg iOS 18.1, Android 13 48 | validations: 49 | required: true 50 | - type: input 51 | id: framework-version 52 | attributes: 53 | label: React Native / Expo version 54 | description: What version React Native / Expo are you using 55 | placeholder: "eg: 0.76.6, Expo 52 (consult your package.json)" 56 | - type: textarea 57 | id: what-happened 58 | attributes: 59 | label: What happened? 60 | description: Also tell us, what did you expect to happen? 61 | placeholder: Tell us what happened and what did you expect to happen. 62 | validations: 63 | required: true 64 | - type: textarea 65 | id: config 66 | attributes: 67 | label: Plugin Code and/or Config 68 | description: Provide the Config you're using with the `BackgroundFetch.configure(config)` method along with any other relevant code. 69 | placeholder: "Paste your Config or code here, eg: BackgroundFetch.configure(config).\n\nYour code will be automatically syntax-highlighted" 70 | render: typescript 71 | validations: 72 | required: true 73 | - type: textarea 74 | id: logs 75 | attributes: 76 | label: Relevant log output 77 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 78 | placeholder: "include iOS / Android logs\n- $ adb logcat -s TSBackgroundFetch:V\n- ios XCode logs" 79 | render: shell 80 | 81 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-help-wanted.yml: -------------------------------------------------------------------------------- 1 | name: Help Wanted 2 | description: I need assistance 3 | title: "[Help Wanted]: " 4 | labels: ["triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this support request. 10 | - type: checkboxes 11 | id: terms 12 | attributes: 13 | label: Required Reading 14 | description: "- I have read the [README](../blob/master/README.md) and [Debugging](../blob/master/README.md#debugging-1) section\n- I am aware of the [API Documentation](../blob/master/README.md#api-documentation)\n- I am aware of the [CHANGELOG](../blob/master/CHANGELOG.md)" 15 | options: 16 | - label: Confirmed 17 | required: true 18 | - type: input 19 | id: version 20 | attributes: 21 | label: Plugin Version 22 | description: What version of our software are you running? 23 | placeholder: "Consult your package.json (do not enter 'latest')" 24 | validations: 25 | required: true 26 | - type: checkboxes 27 | id: os 28 | attributes: 29 | label: Mobile operating-system(s) 30 | description: Which mobile operating-system(s) is this about? 31 | options: 32 | - label: iOS 33 | - label: Android 34 | validations: 35 | required: true 36 | - type: textarea 37 | id: what-happened 38 | attributes: 39 | label: What do you require assistance about? 40 | validations: 41 | required: true 42 | - type: textarea 43 | id: config 44 | attributes: 45 | label: "[Optional] Plugin Code and/or Config" 46 | description: Provide the Config you're using with the `BackgroundFetch.configure(config)` method along with any other relevant code. 47 | placeholder: "Paste your Config or code here, eg: BackgroundFetch.configure(config).\n\nYour code will be automatically syntax-highlighted" 48 | render: typescript 49 | validations: 50 | required: false 51 | - type: textarea 52 | id: logs 53 | attributes: 54 | label: "[Optional] Relevant log output" 55 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 56 | placeholder: "include iOS / Android logs\n- $ adb logcat -s TSBackgroundFetch:V\n- ios XCode logs" 57 | render: shell 58 | validations: 59 | required: false 60 | 61 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v5 14 | with: 15 | operations-per-run: 100 16 | days-before-issue-stale: 30 17 | days-before-issue-close: 14 18 | stale-issue-label: "stale" 19 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." 20 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." 21 | days-before-pr-stale: -1 22 | days-before-pr-close: -1 23 | repo-token: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | package-lock.json 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | 41 | .DS_Store 42 | 43 | android/build 44 | android/*.iml 45 | 46 | xcuserdata/ 47 | android/.gradle 48 | 49 | android/local.properties 50 | 51 | # Expo build 52 | expo/plugin/build/** 53 | 54 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | docs/ 3 | example/ 4 | *.md 5 | node_modules 6 | .github/ 7 | expo/plugin/src 8 | expo/plugin/tsconfig.json 9 | -------------------------------------------------------------------------------- /.yarnignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | docs/ 3 | example/ 4 | *.md 5 | node_modules 6 | .github/ 7 | 8 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | ## Your Environment 16 | * Plugin version: 17 | * Platform: iOS or Android 18 | * OS version: 19 | * Device manufacturer / model: 20 | * React Native version (`react-native -v`): 21 | * Plugin config 22 | 23 | ## Expected Behavior 24 | 25 | 26 | ## Actual Behavior 27 | 28 | 29 | ## Steps to Reproduce 30 | 31 | 1. 32 | 2. 33 | 3. 34 | 4. 35 | 36 | ## Context 37 | 38 | 39 | ## Debug logs 40 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Transistor Software 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RNBackgroundFetch.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.cocoapods_version = '>= 1.10.0' 7 | s.name = 'RNBackgroundFetch' 8 | s.version = package['version'] 9 | s.summary = package['description'] 10 | s.description = <<-DESC 11 | iOS BackgroundFetch API Implementation 12 | DESC 13 | s.homepage = package['homepage'] 14 | s.license = package['license'] 15 | s.author = package['author'] 16 | s.source = { :git => 'https://github.com/transistorsoft/react-native-background-fetch.git', :tag => s.version } 17 | 18 | s.requires_arc = true 19 | s.platform = :ios, '8.0' 20 | 21 | s.dependency 'React-Core' 22 | s.preserve_paths = 'docs', 'CHANGELOG.md', 'LICENSE', 'package.json', 'RNBackgroundFetch.ios.js' 23 | s.source_files = 'ios/RNBackgroundFetch/RNBackgroundFetch.h', 'ios/RNBackgroundFetch/RNBackgroundFetch.m' 24 | s.vendored_frameworks = 'ios/RNBackgroundFetch/TSBackgroundFetch.xcframework' 25 | s.resource_bundles = {'TSBackgroundFetchPrivacy' => ['ios/Resources/PrivacyInfo.xcprivacy']} 26 | end 27 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def DEFAULT_COMPILE_SDK_VERSION = 31 4 | def DEFAULT_TARGET_SDK_VERSION = 30 5 | def DEFAULT_MIN_SDK_VERSION = 16 6 | def DEFAULT_LIFE_CYCLE_RUNTIME_VERSION = "2.4.1" 7 | def DEFAULT_LIFE_CYCLE_EXTENSIONS_VERSION = "2.2.0" 8 | 9 | def safeExtGet(prop, fallback) { 10 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 11 | } 12 | 13 | android { 14 | if (project.android.hasProperty("namespace")) { 15 | namespace("com.transistorsoft.rnbackgroundfetch") 16 | } 17 | 18 | compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION) 19 | 20 | defaultConfig { 21 | minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION) 22 | targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION) 23 | consumerProguardFiles 'proguard-rules.pro' 24 | versionCode 1 25 | versionName "1.0" 26 | } 27 | compileOptions { 28 | sourceCompatibility JavaVersion.VERSION_1_8 29 | targetCompatibility JavaVersion.VERSION_1_8 30 | } 31 | } 32 | 33 | repositories{ 34 | mavenCentral() 35 | maven { 36 | url './libs' 37 | } 38 | } 39 | 40 | dependencies { 41 | def lifeCycleRuntimeVersion = safeExtGet('lifeCycleRuntimeVersion', DEFAULT_LIFE_CYCLE_RUNTIME_VERSION) 42 | def lifeCycleExtensionsVersion = safeExtGet('lifeCycleExtensionsVersion', DEFAULT_LIFE_CYCLE_EXTENSIONS_VERSION) 43 | 44 | implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" 45 | implementation(group: 'com.transistorsoft', name:'tsbackgroundfetch', version: '+') 46 | // LifeCycleObserver 47 | implementation "androidx.lifecycle:lifecycle-runtime:$lifeCycleRuntimeVersion" 48 | implementation "androidx.lifecycle:lifecycle-extensions:$lifeCycleExtensionsVersion" 49 | } 50 | -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/transistorsoft/react-native-background-fetch/f682dbd0e1d4e1c943d032b66246e40e118ebec0/android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.aar -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.aar.md5: -------------------------------------------------------------------------------- 1 | 8179b611fd9f6fb07357b9b7dfde781b -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.aar.sha1: -------------------------------------------------------------------------------- 1 | eb0a4f6a810f05eec51d5b92b846a70693c52be6 -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.aar.sha256: -------------------------------------------------------------------------------- 1 | 7678cef9a5ff3942720e2839aa82473a81e06ba5e0ca672cfaa2100a98b66468 -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.aar.sha512: -------------------------------------------------------------------------------- 1 | 7fa2f89c2a79cf5ff6070e3986914fff7f46d91dfe40ab21a70875556f429bffb99b085c59b10951063cb717896f94fb7a478611607fb544eb838a87470bea1c -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.pom: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.transistorsoft 6 | tsbackgroundfetch 7 | 1.0.4 8 | aar 9 | 10 | -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.pom.md5: -------------------------------------------------------------------------------- 1 | d17405d8d49a051f96c9e960f7059109 -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.pom.sha1: -------------------------------------------------------------------------------- 1 | 1fd4138746b94af71350424a33c0160c154812d9 -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.pom.sha256: -------------------------------------------------------------------------------- 1 | 3702c65bb77f6c0741f6005e770968fb015877378f3a1c8b8e7c31f47da65aa1 -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/1.0.4/tsbackgroundfetch-1.0.4.pom.sha512: -------------------------------------------------------------------------------- 1 | 820e817adf42044cbab55e028a9d142741ae2b211c829e145813ad720320d92f118136f73835f096123168a18fbabf5c056f3e57fe0f95b403a682ce73854dd7 -------------------------------------------------------------------------------- /android/libs/com/transistorsoft/tsbackgroundfetch/maven-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.transistorsoft 4 | tsbackgroundfetch 5 | 6 | 1.0.4 7 | 1.0.4 8 | 9 | 1.0.2 10 | 1.0.3 11 | 1.0.4 12 | 13 | 20241128152535 14 | 15 | 16 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # [react-native-background-fetch] 2 | -keep class com.transistorsoft.rnbackgroundfetch.HeadlessTask { *; } 3 | 4 | # for react-native Headless on new architecture 5 | -keep class com.facebook.react.defaults.DefaultNewArchitectureEntryPoint { 6 | public ; 7 | } 8 | -keep class com.facebook.react.ReactApplication { 9 | public ; 10 | } 11 | -keep class com.facebook.react.ReactHost { 12 | public ; 13 | } 14 | -keep class * extends com.facebook.react.ReactHost { 15 | public ; 16 | } 17 | -keep class com.facebook.react.fabric.** { *; } 18 | 19 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/src/main/java/com/transistorsoft/rnbackgroundfetch/HeadlessTask.java: -------------------------------------------------------------------------------- 1 | package com.transistorsoft.rnbackgroundfetch; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.util.Log; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.annotation.Nullable; 9 | 10 | import com.facebook.infer.annotation.Assertions; 11 | import com.facebook.react.ReactApplication; 12 | import com.facebook.react.ReactInstanceEventListener; 13 | import com.facebook.react.ReactInstanceManager; 14 | import com.facebook.react.ReactNativeHost; 15 | import com.facebook.react.bridge.ReactContext; 16 | import com.facebook.react.bridge.UiThreadUtil; 17 | import com.facebook.react.bridge.WritableMap; 18 | import com.facebook.react.bridge.WritableNativeMap; 19 | import com.facebook.react.jstasks.HeadlessJsTaskConfig; 20 | import com.facebook.react.jstasks.HeadlessJsTaskContext; 21 | 22 | import com.transistorsoft.tsbackgroundfetch.BGTask; 23 | import com.transistorsoft.tsbackgroundfetch.BackgroundFetch; 24 | import com.facebook.react.common.LifecycleState; 25 | 26 | import java.lang.reflect.Method; 27 | 28 | /** 29 | * Created by chris on 2018-01-17. 30 | */ 31 | 32 | public class HeadlessTask { 33 | private static final String HEADLESS_TASK_NAME = "BackgroundFetch"; 34 | private static final Handler mHandler = new Handler(); 35 | 36 | private final BGTask mBGTask; 37 | public HeadlessTask(Context context, BGTask task) { 38 | mBGTask = task; 39 | WritableMap clientEvent = new WritableNativeMap(); 40 | clientEvent.putString("taskId", task.getTaskId()); 41 | clientEvent.putBoolean("timeout", task.getTimedOut()); 42 | HeadlessJsTaskConfig config = new HeadlessJsTaskConfig(HEADLESS_TASK_NAME, clientEvent, 30000); 43 | try { 44 | startTask(config, context); 45 | } catch (AssertionError e) { 46 | Log.d(BackgroundFetch.TAG, "[HeadlessTask] Failed invoke HeadlessTask: " + e.getMessage()); 47 | } 48 | } 49 | 50 | /** 51 | * Start a task. This method handles starting a new React instance if required. 52 | * 53 | * Has to be called on the UI thread. 54 | * 55 | * @param taskConfig describes what task to start and the parameters to pass to it 56 | */ 57 | protected void startTask(final HeadlessJsTaskConfig taskConfig, Context context) { 58 | UiThreadUtil.assertOnUiThread(); 59 | 60 | ReactContext reactContext = getReactContext(context); 61 | 62 | if (reactContext == null) { 63 | createReactContextAndScheduleTask(taskConfig, context); 64 | } else { 65 | invokeStartTask(reactContext, taskConfig); 66 | } 67 | } 68 | 69 | private void invokeStartTask(ReactContext reactContext, final HeadlessJsTaskConfig taskConfig) { 70 | if (reactContext.getLifecycleState() == LifecycleState.RESUMED) { 71 | return; 72 | } 73 | final HeadlessJsTaskContext headlessJsTaskContext = HeadlessJsTaskContext.getInstance(reactContext); 74 | 75 | UiThreadUtil.runOnUiThread(() -> { 76 | try { 77 | final int taskId = headlessJsTaskContext.startTask(taskConfig); 78 | Log.d(BackgroundFetch.TAG, "[HeadlessTask] start taskId: " + taskId); 79 | // Add a BGTask.finish(taskId) listener. This is executed when the user runs BackgroundFetch.finish(taskId). 80 | // We use this to finish the RN headless task. 81 | mBGTask.setCompletionHandler(() -> { 82 | Log.d(BackgroundFetch.TAG, "[HeadlessTask] end taskId: " + taskId); 83 | if (headlessJsTaskContext.isTaskRunning(taskId)) { 84 | headlessJsTaskContext.finishTask(taskId); 85 | } 86 | }); 87 | } catch (IllegalStateException exception) { 88 | Log.e(BackgroundFetch.TAG, "[HeadlessTask] task attempted to run in the foreground. Task ignored."); 89 | } 90 | }); 91 | } 92 | 93 | private ReactNativeHost getReactNativeHost(Context context) { 94 | return ((ReactApplication) context.getApplicationContext()).getReactNativeHost(); 95 | } 96 | 97 | /** 98 | * Get the {ReactHost} used by this app. ure and returns null if not. 99 | */ 100 | private @Nullable Object getReactHost(Context context) { 101 | context = context.getApplicationContext(); 102 | try { 103 | Method getReactHost = context.getClass().getMethod("getReactHost"); 104 | return getReactHost.invoke(context); 105 | // Original non-reflection return: 106 | //return ((ReactApplication) context.getApplicationContext()).getReactHost(); 107 | } catch (Exception e) { 108 | Log.d(BackgroundFetch.TAG, "[HeadlessTask] Reflection error ReactHost: " + e); 109 | return null; 110 | } 111 | } 112 | 113 | private ReactContext getReactContext(Context context) { 114 | if (isBridglessArchitectureEnabled()) { 115 | Object reactHost = getReactHost(context); 116 | Assertions.assertNotNull(reactHost, "getReactHost() is null in New Architecture"); 117 | try { 118 | Method getCurrentReactContext = reactHost.getClass().getMethod("getCurrentReactContext"); 119 | return (ReactContext) getCurrentReactContext.invoke(reactHost); 120 | } catch (Exception e) { 121 | Log.e(BackgroundFetch.TAG, "[HeadlessTask] Reflection error getCurrentReactContext: " + e); 122 | } 123 | } 124 | final ReactInstanceManager reactInstanceManager = getReactNativeHost(context).getReactInstanceManager(); 125 | return reactInstanceManager.getCurrentReactContext(); 126 | } 127 | 128 | private void createReactContextAndScheduleTask(final HeadlessJsTaskConfig taskConfig, Context context) { 129 | Log.d(BackgroundFetch.TAG, "[HeadlessTask] initializing ReactContext"); 130 | 131 | if (isBridglessArchitectureEnabled()) { // new arch 132 | final Object reactHost = getReactHost(context); 133 | 134 | ReactInstanceEventListener callback = new ReactInstanceEventListener() { 135 | @Override 136 | public void onReactContextInitialized(@NonNull ReactContext reactContext) { 137 | mHandler.postDelayed(() -> invokeStartTask(reactContext, taskConfig), 500); 138 | try { 139 | Method removeReactInstanceEventListener = reactHost.getClass().getMethod("removeReactInstanceEventListener", ReactInstanceEventListener.class); 140 | removeReactInstanceEventListener.invoke(reactHost, this); 141 | } catch (Exception e) { 142 | Log.e(BackgroundFetch.TAG, "[HeadlessTask] reflection error removeReactInstanceEventListener" + e); 143 | } 144 | } 145 | }; 146 | 147 | try { 148 | Method addReactInstanceEventListener = reactHost.getClass().getMethod("addReactInstanceEventListener", ReactInstanceEventListener.class); 149 | addReactInstanceEventListener.invoke(reactHost, callback); 150 | Method startReactHost = reactHost.getClass().getMethod("start"); 151 | startReactHost.invoke(reactHost); 152 | } catch (Exception e) { 153 | Log.e(BackgroundFetch.TAG, "[HeadlessTask] reflection error addReactInstanceEventListener: " + e); 154 | } 155 | } else { // old arch 156 | final ReactInstanceManager reactInstanceManager = getReactNativeHost(context).getReactInstanceManager(); 157 | reactInstanceManager.addReactInstanceEventListener(new ReactInstanceEventListener() { 158 | @Override 159 | public void onReactContextInitialized(@NonNull ReactContext reactContext) { 160 | mHandler.postDelayed(() -> invokeStartTask(reactContext, taskConfig), 500); 161 | reactInstanceManager.removeReactInstanceEventListener(this); 162 | } 163 | }); 164 | reactInstanceManager.createReactContextInBackground(); 165 | } 166 | } 167 | 168 | // Returns true if the app has enabled bridgeless mode. Thanks to @mikehardy for this block. 169 | private boolean isBridglessArchitectureEnabled() { 170 | try { 171 | Class entryPoint = Class.forName("com.facebook.react.defaults.DefaultNewArchitectureEntryPoint"); 172 | Method bridgelessEnabled = entryPoint.getMethod("getBridgelessEnabled"); 173 | Object result = bridgelessEnabled.invoke(null); 174 | return (result == Boolean.TRUE); 175 | } catch (Exception e) { 176 | return false; 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /android/src/main/java/com/transistorsoft/rnbackgroundfetch/RNBackgroundFetchModule.java: -------------------------------------------------------------------------------- 1 | package com.transistorsoft.rnbackgroundfetch; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.util.Log; 6 | 7 | import com.facebook.react.bridge.*; 8 | import com.facebook.react.modules.core.RCTNativeAppEventEmitter; 9 | import com.transistorsoft.tsbackgroundfetch.BackgroundFetch; 10 | import com.transistorsoft.tsbackgroundfetch.BackgroundFetchConfig; 11 | import com.transistorsoft.tsbackgroundfetch.LifecycleManager; 12 | 13 | public class RNBackgroundFetchModule extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener { 14 | public static final String TAG = "RNBackgroundFetch"; 15 | private static final String EVENT_FETCH = "fetch"; 16 | private static final String JOB_SERVICE_CLASS = HeadlessTask.class.getName(); 17 | private static final String FETCH_TASK_ID = "react-native-background-fetch"; 18 | private boolean initialized = false; 19 | 20 | public RNBackgroundFetchModule(ReactApplicationContext reactContext) { 21 | super(reactContext); 22 | Log.d(BackgroundFetch.TAG, "[RNBackgroundFetch initialize]"); 23 | BackgroundFetch.getInstance(reactContext.getApplicationContext()); 24 | reactContext.addLifecycleEventListener(this); 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return TAG; 30 | } 31 | 32 | @ReactMethod 33 | public void configure(ReadableMap options, final Callback success, final Callback failure) { 34 | BackgroundFetch adapter = getAdapter(); 35 | 36 | BackgroundFetch.Callback callback = new BackgroundFetch.Callback() { 37 | @Override public void onFetch(String taskId) { 38 | WritableMap params = new WritableNativeMap(); 39 | params.putString("taskId", taskId); 40 | params.putBoolean("timeout", false); 41 | getReactApplicationContext().getJSModule(RCTNativeAppEventEmitter.class).emit(EVENT_FETCH, params); 42 | } 43 | @Override public void onTimeout(String taskId) { 44 | WritableMap params = new WritableNativeMap(); 45 | params.putString("taskId", taskId); 46 | params.putBoolean("timeout", true); 47 | getReactApplicationContext().getJSModule(RCTNativeAppEventEmitter.class).emit(EVENT_FETCH, params); 48 | } 49 | }; 50 | adapter.configure(buildConfig(options) 51 | .setTaskId(FETCH_TASK_ID) 52 | .setIsFetchTask(true) 53 | .build(), callback); 54 | 55 | success.invoke(BackgroundFetch.STATUS_AVAILABLE); 56 | } 57 | 58 | @ReactMethod 59 | public void scheduleTask(ReadableMap options, final Callback success, final Callback failure) { 60 | BackgroundFetch adapter = getAdapter(); 61 | adapter.scheduleTask(buildConfig(options).build()); 62 | success.invoke(true); 63 | } 64 | 65 | @ReactMethod 66 | public void start(Callback success, Callback failure) { 67 | BackgroundFetch adapter = getAdapter(); 68 | adapter.start(FETCH_TASK_ID); 69 | success.invoke(adapter.status()); 70 | } 71 | 72 | @ReactMethod 73 | public void stop(String taskId, Callback success, Callback failure) { 74 | if (taskId == null) taskId = FETCH_TASK_ID; 75 | BackgroundFetch adapter = getAdapter(); 76 | adapter.stop(taskId); 77 | success.invoke(true); 78 | } 79 | 80 | @ReactMethod 81 | public void status(Callback success) { 82 | BackgroundFetch adapter = getAdapter(); 83 | success.invoke(adapter.status()); 84 | } 85 | 86 | @ReactMethod 87 | public void finish(String taskId) { 88 | BackgroundFetch adapter = getAdapter(); 89 | adapter.finish(taskId); 90 | } 91 | 92 | @ReactMethod 93 | public void addListener(String event) { 94 | // Keep: Required for RN built-in NativeEventEmitter calls. 95 | } 96 | 97 | @ReactMethod 98 | public void removeListeners(Integer count) { 99 | // Keep: Required for RN built-in NativeEventEmitter calls. 100 | } 101 | 102 | @Override 103 | public void onHostResume() { 104 | if (!initialized) { 105 | initializeBackgroundFetch(); 106 | } 107 | } 108 | 109 | @Override 110 | public void onHostPause() { 111 | } 112 | 113 | @Override 114 | public void onNewIntent(Intent intent) { 115 | } 116 | 117 | @Override 118 | public void onHostDestroy() { 119 | LifecycleManager.getInstance().setHeadless(true); 120 | initialized = false; 121 | } 122 | 123 | @Override 124 | public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { 125 | 126 | } 127 | 128 | private BackgroundFetchConfig.Builder buildConfig(ReadableMap options) { 129 | BackgroundFetchConfig.Builder config = new BackgroundFetchConfig.Builder(); 130 | if (options.hasKey(BackgroundFetchConfig.FIELD_MINIMUM_FETCH_INTERVAL)) { 131 | config.setMinimumFetchInterval(options.getInt(BackgroundFetchConfig.FIELD_MINIMUM_FETCH_INTERVAL)); 132 | } 133 | if (options.hasKey(BackgroundFetchConfig.FIELD_TASK_ID)) { 134 | config.setTaskId(options.getString(BackgroundFetchConfig.FIELD_TASK_ID)); 135 | } 136 | if (options.hasKey(BackgroundFetchConfig.FIELD_DELAY)) { 137 | Integer delay = options.getInt(BackgroundFetchConfig.FIELD_DELAY); 138 | config.setDelay(delay.longValue()); 139 | } 140 | if (options.hasKey(BackgroundFetchConfig.FIELD_STOP_ON_TERMINATE)) { 141 | config.setStopOnTerminate(options.getBoolean(BackgroundFetchConfig.FIELD_STOP_ON_TERMINATE)); 142 | } 143 | if (options.hasKey(BackgroundFetchConfig.FIELD_FORCE_ALARM_MANAGER)) { 144 | config.setForceAlarmManager(options.getBoolean(BackgroundFetchConfig.FIELD_FORCE_ALARM_MANAGER)); 145 | } 146 | if (options.hasKey(BackgroundFetchConfig.FIELD_START_ON_BOOT)) { 147 | config.setStartOnBoot(options.getBoolean(BackgroundFetchConfig.FIELD_START_ON_BOOT)); 148 | } 149 | if (options.hasKey("enableHeadless") && options.getBoolean("enableHeadless")) { 150 | config.setJobService(JOB_SERVICE_CLASS); 151 | } 152 | if (options.hasKey(BackgroundFetchConfig.FIELD_REQUIRED_NETWORK_TYPE)) { 153 | config.setRequiredNetworkType(options.getInt(BackgroundFetchConfig.FIELD_REQUIRED_NETWORK_TYPE)); 154 | } 155 | if (options.hasKey(BackgroundFetchConfig.FIELD_REQUIRES_BATTERY_NOT_LOW)) { 156 | config.setRequiresBatteryNotLow(options.getBoolean(BackgroundFetchConfig.FIELD_REQUIRES_BATTERY_NOT_LOW)); 157 | } 158 | if (options.hasKey(BackgroundFetchConfig.FIELD_REQUIRES_CHARGING)) { 159 | config.setRequiresCharging(options.getBoolean(BackgroundFetchConfig.FIELD_REQUIRES_CHARGING)); 160 | } 161 | if (options.hasKey(BackgroundFetchConfig.FIELD_REQUIRES_DEVICE_IDLE)) { 162 | config.setRequiresDeviceIdle(options.getBoolean(BackgroundFetchConfig.FIELD_REQUIRES_DEVICE_IDLE)); 163 | } 164 | if (options.hasKey(BackgroundFetchConfig.FIELD_REQUIRES_STORAGE_NOT_LOW)) { 165 | config.setRequiresStorageNotLow(options.getBoolean(BackgroundFetchConfig.FIELD_REQUIRES_STORAGE_NOT_LOW)); 166 | } 167 | if (options.hasKey(BackgroundFetchConfig.FIELD_PERIODIC)) { 168 | config.setPeriodic(options.getBoolean(BackgroundFetchConfig.FIELD_PERIODIC)); 169 | } 170 | return config; 171 | } 172 | 173 | private void initializeBackgroundFetch() { 174 | Activity activity = getCurrentActivity(); 175 | if (activity == null) { 176 | return; 177 | } 178 | initialized = true; 179 | } 180 | 181 | private BackgroundFetch getAdapter() { 182 | return BackgroundFetch.getInstance(getReactApplicationContext()); 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /android/src/main/java/com/transistorsoft/rnbackgroundfetch/RNBackgroundFetchPackage.java: -------------------------------------------------------------------------------- 1 | package com.transistorsoft.rnbackgroundfetch; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.JavaScriptModule; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | public class RNBackgroundFetchPackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules (ReactApplicationContext reactContext) { 16 | List modules = new ArrayList<>(); 17 | modules.add(new RNBackgroundFetchModule(reactContext)); 18 | return modules; 19 | } 20 | 21 | public List> createJSModules() { 22 | return Collections.emptyList(); 23 | } 24 | 25 | @Override 26 | public List createViewManagers(ReactApplicationContext reactContext) { 27 | return Collections.emptyList(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app.plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./expo/plugin/build'); 2 | -------------------------------------------------------------------------------- /docs/INSTALL-AUTO-ANDROID.md: -------------------------------------------------------------------------------- 1 | # Android Auto-linking Setup 2 | 3 | ### `react-native >= 0.60` 4 | 5 | ### With `yarn` 6 | 7 | ```bash 8 | $ yarn add react-native-background-fetch 9 | ``` 10 | 11 | ### With `npm` 12 | ```bash 13 | $ npm install --save react-native-background-fetch 14 | ``` 15 | 16 | ## Gradle Configuration 17 | 18 | The SDK requires a custom __`maven url`__ in the root __`android/build.gradle`__. 19 | Please note that some more recent versions of React Native the Android template may not include __`allprojects`__ section. You should add this manually as a separate section along with the nested __`repositories`__ section in the same __`android/build.gradle`__ file. 20 | 21 | ### :open_file_folder: **`android/build.gradle`** 22 | 23 | ```diff 24 | allprojects { 25 | repositories { 26 | mavenLocal() 27 | maven { 28 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 29 | url("$rootDir/../node_modules/react-native/android") 30 | } 31 | maven { 32 | // Android JSC is installed from npm 33 | url("$rootDir/../node_modules/jsc-android/dist") 34 | } 35 | + maven { 36 | + // react-native-background-fetch 37 | + url("${project(':react-native-background-fetch').projectDir}/libs") 38 | + } 39 | 40 | } 41 | } 42 | ``` 43 | 44 | ## Precise event-scheduling with `forceAlarmManager: true`: 45 | 46 | **Only** If you wish to use precise scheduling of events with __`forceAlarmManager: true`__, *Android 14 (SDK 34)*, has restricted usage of ["`AlarmManager` exact alarms"](https://developer.android.com/about/versions/14/changes/schedule-exact-alarms). To continue using precise timing of events with *Android 14*, you can manually add this permission to your __`AndroidManifest`__. Otherwise, the plugin will gracefully fall-back to "*in-exact* `AlarmManager` scheduling": 47 | 48 | :open_file_folder: In your `AndroidManifest`, add the following permission (**exactly as-shown**): 49 | 50 | ```xml 51 | 52 | 53 | . 54 | . 55 | . 56 | 57 | ``` 58 | :warning: It has been announced that *Google Play Store* [has plans to impose greater scrutiny](https://support.google.com/googleplay/android-developer/answer/13161072?sjid=3640341614632608469-NA) over usage of this permission (which is why the plugin does not automatically add it). 59 | 60 | -------------------------------------------------------------------------------- /docs/INSTALL-AUTO-IOS.md: -------------------------------------------------------------------------------- 1 | # iOS Auto-linking Setup 2 | ### `react-native >= 0.60` 3 | 4 | ### With `yarn` 5 | 6 | ```bash 7 | $ yarn add react-native-background-fetch 8 | ``` 9 | 10 | ### With `npm` 11 | ```bash 12 | $ npm install --save react-native-background-fetch 13 | ``` 14 | 15 | ## `pod install` 16 | 17 | **:warning: requires *cocoapods* `>= 1.10+`:** 18 | 19 | ```bash 20 | $ pod --version 21 | // if < 1.10.0 22 | $ sudo gem install cocoapods 23 | ``` 24 | 25 | ```bash 26 | $ cd ios 27 | $ pod install 28 | ``` 29 | 30 | ## Configure Background Capabilities 31 | 32 | - Select the root of your project. Select **Capabilities** tab. Enable **Background Modes** and enable the following mode: 33 | 34 | - [x] Background fetch 35 | - [x] Background processing (:new: __iOS 13+__; Only if you intend to use `BackgroundFetch.scheduleTask`) 36 | 37 | ![](https://dl.dropboxusercontent.com/s/9vik5kxoklk63ob/ios-setup-background-modes.png?dl=1) 38 | 39 | 40 | ## Configure `Info.plist` (:new: __iOS 13+__) 41 | 1. Open your `Info.plist` and add the key *"Permitted background task scheduler identifiers"* 42 | 43 | ![](https://dl.dropboxusercontent.com/s/t5xfgah2gghqtws/ios-setup-permitted-identifiers.png?dl=1) 44 | 45 | 2. Add the **required identifier `com.transistorsoft.fetch`**. 46 | 47 | ![](https://dl.dropboxusercontent.com/s/kwdio2rr256d852/ios-setup-permitted-identifiers-add.png?dl=1) 48 | 49 | 3. If you intend to execute your own custom tasks via **`BackgroundFetch.scheduleTask`**, you must add those custom identifiers as well. For example, if you intend to execute a custom **`taskId: 'com.transistorsoft.customtask'`**, you must add the identifier **`com.transistorsoft.customtask`** to your *"Permitted background task scheduler identifiers"*, as well. 50 | 51 | :warning: A task identifier can be any string you wish, but it's a good idea to prefix them now with `com.transistorsoft.` — In the future, the `com.transistorsoft` prefix **may become required**. 52 | 53 | ```javascript 54 | BackgroundFetch.scheduleTask({ 55 | taskId: 'com.transistorsoft.customtask', 56 | delay: 60 * 60 * 1000 // In one hour (milliseconds) 57 | }); 58 | ``` 59 | 60 | ## Privacy Manifest 61 | 62 | Apple now requires apps provide a [Privacy Manifest for "sensitive" APIs](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api?language=objc) which could be abused for "fingerprinting" a user for malicious marketing activity. 63 | 64 | If your app does not yet have a *Privacy Manifest* (__`PrivacyInfo.xcprivacy`__), create one now: 65 | 66 |
67 | ℹ️ Click here for detailed instructions... 68 | 69 | - In XCode, __`File -> New -> File...`__: 70 | 71 | ![](https://dl.dropboxusercontent.com/scl/fi/n28028i3fbrxd67u491w2/file-new-PrivacyInfo.png?rlkey=sc7s1lyy8fli2c1hz2cfa4cpm&dl=1) 72 | 73 | - Be sure to enable your `Targets: [x] YourApp`: 74 | 75 | ![](https://dl.dropboxusercontent.com/scl/fi/pmbfn5jypvns6r5pyhnui/file-new-PrivacyInfo-targets.png?rlkey=epvjffar23bxgyi9xax9ys40i&dl=1) 76 | 77 | 78 |
79 | 80 | 81 | It's best to edit this file's XML manually. 82 | - :open_file_folder: `ios/PrivacyInfo.xcprivacy` 83 | - Add the following block within the `NSPrivacyAccessedAPITypes` `` container: 84 | 85 | ```xml 86 | 87 | 88 | 89 | 90 | 91 | NSPrivacyAccessedAPITypes 92 | 93 | 94 | 95 | NSPrivacyAccessedAPIType 96 | NSPrivacyAccessedAPICategoryUserDefaults 97 | 98 | NSPrivacyAccessedAPITypeReasons 99 | 100 | CA92.1 101 | 102 | 103 | 104 | 105 | 106 | ``` 107 | 108 | ## `AppDelegate` 109 | :open_file_folder: __`AppDelegate.m`__ 110 | 111 | ```diff 112 | #import "AppDelegate.h" 113 | 114 | #import 115 | #import 116 | #import 117 | 118 | // IMPORTANT: Paste import ABOVE the DEBUG macro 119 | +#import 120 | 121 | #if DEBUG 122 | . 123 | . /////////////////////////////////////////////////////////////////////////////////// 124 | . // IMPORTANT: DO NOT paste import within DEBUG macro or archiving will fail!!! 125 | . /////////////////////////////////////////////////////////////////////////////////// 126 | . 127 | #endif 128 | 129 | @implementation AppDelegate 130 | 131 | (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 132 | . 133 | . 134 | . 135 | + // [REQUIRED] Register BackgroundFetch 136 | + [[TSBackgroundFetch sharedInstance] didFinishLaunching]; 137 | 138 | return YES; 139 | } 140 | ``` 141 | 142 | #### Or if you're using `Swift`: 143 | :open_file_folder: __`AppDelegate.swift`__: 144 | 145 | ```diff 146 | import Foundation 147 | import UIKit 148 | +import TSBackgroundFetch 149 | 150 | @UIApplicationMain 151 | class AppDelegate: RCTAppDelegate { 152 | 153 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 154 | . 155 | . 156 | . 157 | + // [REQUIRED] Register BackgroundFetch 158 | + TSBackgroundFetch.sharedInstance().didFinishLaunching(); 159 | return self.application(application, didFinishLaunchingWithOptions: launchOptions); 160 | } 161 | } 162 | ``` 163 | 164 | ## BackgroundFetch AppDelegate extension 165 | 166 | :warning: Deprecated iOS Background Fetch API for devices running __`< iOS 13`__. 167 | 168 | BackgroundFetch implements an `AppDelegate` method `didPerformFetchWithCompletionHandler`. You must manually add this file to the same folder where your `AppDelegate.m` lives: 169 | 170 | - In the XCode's **`Project navigator`**, right click on project's name ➜ **`Add Files to <...>`**. 171 | - **`node_modules/react-native-background-fetch/ios/RNBackgroundFetch/RNBackgroundFetch+AppDelegate.m`**. 172 | 173 | ![](https://dl.dropbox.com/s/rwn8kyo8fgdn57u/autolinking-step1.png?dl=1) 174 | 175 | **`node_modules/react-native-background-fetch/ios/RNBackgroundFetch/RNBackgroundFetch+AppDelegate.m`** 176 | ![](https://dl.dropbox.com/s/r4f564giaz257fw/autolinking-step2.png?dl=1) 177 | 178 | 179 | -------------------------------------------------------------------------------- /docs/INSTALL-COCOAPODS-IOS.md: -------------------------------------------------------------------------------- 1 | # iOS Installation with CocoaPods 2 | 3 | ## With `yarn` 4 | 5 | ```bash 6 | $ yarn add react-native-background-fetch 7 | ``` 8 | 9 | ## With `npm` 10 | ```bash 11 | $ npm install --save react-native-background-fetch 12 | ``` 13 | 14 | ### `Podfile` 15 | 16 | If you **don't** already have an `ios/Podfile` initialized, run the following command: 17 | ```shell 18 | $ cd ios 19 | $ pod init 20 | ``` 21 | 22 | Replace the contents of `Podfile` with the following, substituting `_YOUR_PROJECT_TARGET`: 23 | ```ruby 24 | # Uncomment the next line to define a global platform for your project 25 | # platform :ios, '9.0' 26 | 27 | target '_YOUR_PROJECT_TARGET_' do 28 | rn_path = '../node_modules/react-native' 29 | 30 | # See http://facebook.github.io/react-native/docs/integration-with-existing-apps.html#configuring-cocoapods-dependencies 31 | pod 'yoga', path: "#{rn_path}/ReactCommon/yoga/yoga.podspec" 32 | pod 'React', path: rn_path, subspecs: [ 33 | 'Core', 34 | 'CxxBridge', 35 | 'DevSupport', 36 | 'RCTActionSheet', 37 | 'RCTAnimation', 38 | 'RCTGeolocation', 39 | 'RCTImage', 40 | 'RCTLinkingIOS', 41 | 'RCTNetwork', 42 | 'RCTSettings', 43 | 'RCTText', 44 | 'RCTVibration', 45 | 'RCTWebSocket', 46 | ] 47 | 48 | # React Native third party dependencies podspecs 49 | pod 'DoubleConversion', :podspec => "#{rn_path}/third-party-podspecs/DoubleConversion.podspec" 50 | pod 'glog', :podspec => "#{rn_path}/third-party-podspecs/glog.podspec" 51 | # If you are using React Native <0.54, you will get the following error: 52 | # "The name of the given podspec `GLog` doesn't match the expected one `glog`" 53 | # Use the following line instead: 54 | #pod 'GLog', :podspec => "#{rn_path}/third-party-podspecs/GLog.podspec" 55 | pod 'Folly', :podspec => "#{rn_path}/third-party-podspecs/Folly.podspec" 56 | 57 | end 58 | 59 | post_install do |installer| 60 | installer.pods_project.targets.each do |target| 61 | if target.name == "React" 62 | target.remove_from_project 63 | end 64 | end 65 | end 66 | 67 | ``` 68 | 69 | ### `react-native link` 70 | 71 | Running `react-native link` will automatically install the plugin into your `Podfile`. 72 | 73 | - Follow the [`react-native link` instructions](./INSTALL-LINK-IOS.md). 74 | - Install the new CocoaPods: 75 | 76 | ```shell 77 | $ cd ios 78 | $ pod install 79 | ``` 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /docs/INSTALL-EXPO.md: -------------------------------------------------------------------------------- 1 | # Expo Setup 2 | 3 | ```bash 4 | npx expo install react-native-background-fetch 5 | ``` 6 | 7 | ### :open_file_folder: **`app.json`** 8 | 9 | - Add the following to __`plugins`__: 10 | 11 | ```diff 12 | { 13 | "expo": { 14 | "name": "your-app-name", 15 | "plugins": [ 16 | + "react-native-background-fetch" 17 | ] 18 | } 19 | } 20 | ``` 21 | 22 | - Add the following __`UIBackgroundModes`__ and __`BGTaskSchedulerPermittedIdentifiers`__ to the __`ios.infoPlist`__ section: 23 | 24 | 25 | ```diff 26 | { 27 | "expo": { 28 | "name": "your-app-name", 29 | "plugins": [ 30 | "react-native-background-fetch" 31 | ], 32 | "ios": { 33 | + "infoPlist": { 34 | + "UIBackgroundModes": [ 35 | + "fetch", 36 | + "processing" 37 | + ], 38 | + "BGTaskSchedulerPermittedIdentifiers": [ 39 | + "com.transistorsoft.fetch" 40 | + ] 41 | + } 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | - If you intend to execute your own custom tasks via **`BackgroundFetch.scheduleTask`**, you must add those custom identifiers as well to the __`BGTaskSchedulerPermittedIdentifiers`__. For example, if you intend to execute a custom **`taskId: 'com.transistorsoft.customtask'`**, you must add the identifier **`com.transistorsoft.customtask`** to `BGTaskSchedulerPermittedIdentifiers`: 48 | 49 | ```diff 50 | "BGTaskSchedulerPermittedIdentifiers": [ 51 | "com.transistorsoft.fetch", 52 | + "com.transistorsoft.customtask" 53 | ] 54 | ``` 55 | 56 | :warning: A task identifier can be any string you wish, but it's a good idea to prefix them now with `com.transistorsoft.` — In the future, the `com.transistorsoft` prefix **may become required**. 57 | 58 | ```javascript 59 | BackgroundFetch.scheduleTask({ 60 | taskId: 'com.transistorsoft.customtask', 61 | delay: 60 * 60 * 1000 // In one hour (milliseconds) 62 | }); 63 | ``` 64 | 65 | ### Re-build 66 | 67 | You must rebuild your Android app for the added plugins to be evaluated. 68 | 69 | - If you're developing locally: 70 | 71 | ```bash 72 | npx expo prebuild 73 | ``` 74 | 75 | - If you're using *Expo EAS*: 76 | ```bash 77 | eas build --profile development --platform android 78 | ``` 79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/INSTALL-LINK-ANDROID.md: -------------------------------------------------------------------------------- 1 | # Android `react-native link` Installation 2 | 3 | No manual steps required. 4 | 5 | ### With `yarn` 6 | 7 | ```bash 8 | $ yarn add react-native-background-fetch 9 | ``` 10 | 11 | ### With `npm` 12 | ```bash 13 | $ npm install --save react-native-background-fetch 14 | ``` 15 | 16 | ### `react-native link` 17 | ```bash 18 | $ react-native link react-native-background-fetch 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/INSTALL-LINK-IOS.md: -------------------------------------------------------------------------------- 1 | # iOS Installation with `react-native link` 2 | 3 | ### With `yarn` 4 | 5 | ```bash 6 | $ yarn add react-native-background-fetch 7 | ``` 8 | 9 | ### With `npm` 10 | ```bash 11 | $ npm install --save react-native-background-fetch 12 | ``` 13 | 14 | ### `react-native link` 15 | 16 | ```bash 17 | $ react-native link react-native-background-fetch 18 | ``` 19 | 20 | ### `pod install` 21 | 22 | ```bash 23 | $ cd ios 24 | $ pod install 25 | ``` 26 | 27 | ## Configure Background Capabilities 28 | 29 | - Select the root of your project. Select **Capabilities** tab. Enable **Background Modes** and enable the following mode: 30 | 31 | - [x] Background fetch 32 | - [x] Background processing (:new: __iOS 13+__; Only if you intend to use `BackgroundFetch.scheduleTask`) 33 | 34 | ![](https://dl.dropboxusercontent.com/s/9vik5kxoklk63ob/ios-setup-background-modes.png?dl=1) 35 | 36 | 37 | ## Configure `Info.plist` (:new: __iOS 13+__) 38 | 1. Open your `Info.plist` and the key *"Permitted background task scheduler identifiers"* 39 | 40 | ![](https://dl.dropboxusercontent.com/s/t5xfgah2gghqtws/ios-setup-permitted-identifiers.png?dl=1) 41 | 42 | 2. Add the **required identifier `com.transistorsoft.fetch`**. 43 | 44 | ![](https://dl.dropboxusercontent.com/s/kwdio2rr256d852/ios-setup-permitted-identifiers-add.png?dl=1) 45 | 46 | 3. If you intend to execute your own custom tasks via **`BackgroundFetch.scheduleTask`**, you must add those custom identifiers as well. For example, if you intend to execute a custom **`taskId: 'com.foo.customtask'`**, you must add the identifier **`com.foo.customtask`** to your *"Permitted background task scheduler identifiers"*, as well. 47 | 48 | ```dart 49 | BackgroundFetch.scheduleTask({ 50 | taskId: 'com.foo.customtask', 51 | delay: 60 * 60 * 1000 // In one hour (milliseconds) 52 | }); 53 | ``` 54 | 55 | ## `AppDelegate.m` (:new: __iOS 13+__) 56 | 57 | The [**`BGTaskScheduler`**](https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler?language=objc) API introduced in iOS 13 requires special setup: 58 | 59 | ```diff 60 | +#import 61 | 62 | @implementation AppDelegate 63 | 64 | (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 65 | . 66 | . 67 | . 68 | + // [REQUIRED] Register BackgroundFetch 69 | + [[TSBackgroundFetch sharedInstance] didFinishLaunching]; 70 | 71 | return YES; 72 | } 73 | ``` 74 | 75 | ## BackgroundFetch AppDelegate extension 76 | 77 | :warning: Deprecated iOS Background Fetch API for devices running __`< iOS 13`__. 78 | 79 | BackgroundFetch implements an `AppDelegate` method `didPerformFetchWithCompletionHandler`. You must manually add this file to the same folder where your `AppDelegate.m` lives: 80 | 81 | - In the XCode's **`Project navigator`**, right click on project's name ➜ **`Add Files to <...>`**. 82 | - **`node_modules/react-native-background-fetch/ios/RNBackgroundFetch/RNBackgroundFetch+AppDelegate.m`**. 83 | 84 | ![](https://dl.dropbox.com/s/rwn8kyo8fgdn57u/autolinking-step1.png?dl=1) 85 | 86 | **`node_modules/react-native-background-fetch/ios/RNBackgroundFetch/RNBackgroundFetch+AppDelegate.m`** 87 | ![](https://dl.dropbox.com/s/r4f564giaz257fw/autolinking-step2.png?dl=1) 88 | -------------------------------------------------------------------------------- /docs/INSTALL-MANUAL-ANDROID.md: -------------------------------------------------------------------------------- 1 | # Android Manual Installation 2 | 3 | ## With `yarn` 4 | 5 | ```bash 6 | $ yarn add react-native-background-fetch 7 | ``` 8 | 9 | ## With `npm` 10 | 11 | ```bash 12 | $ npm install --save react-native-background-fetch 13 | ``` 14 | 15 | ## Gradle Configuration 16 | 17 | ### :open_file_folder: **`android/settings.gradle`** 18 | 19 | ```diff 20 | +include ':react-native-background-fetch' 21 | +project(':react-native-background-fetch').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-fetch/android') 22 | ``` 23 | 24 | ------------------------------------------------------------------------------- 25 | 26 | 27 | ### :open_file_folder: **`android/app/build.gradle`** 28 | 29 | ```diff 30 | dependencies { 31 | + implementation project(':react-native-background-fetch') 32 | } 33 | ``` 34 | 35 | 36 | ## MainApplication.java 37 | 38 | ### :open_file_folder: **`android/app/main/java/com/.../MainApplication.java`** 39 | 40 | ```diff 41 | +import com.transistorsoft.rnbackgroundfetch.RNBackgroundFetchPackage; 42 | public class MainApplication extends ReactApplication { 43 | @Override 44 | protected List getPackages() { 45 | return Arrays.asList( 46 | + new RNBackgroundFetchPackage(), 47 | new MainReactPackage() 48 | ); 49 | } 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/INSTALL-MANUAL-IOS.md: -------------------------------------------------------------------------------- 1 | # iOS Manual Installation 2 | 3 | - `npm install react-native-background-fetch --save` 4 | 5 | - In the XCode's **`Project navigator`**, right click on project's name ➜ **`Add Files to <...>`** 6 | ![](https://dl.dropboxusercontent.com/s/nmih1sc9hgygpvu/react-native-background-geolocation-install-1.png?dl=1) 7 | 8 | - Add **`node_modules/react-native-background-fetch/ios/RNBackgroundFetch.xcodeproj`** 9 | ![](https://dl.dropboxusercontent.com/s/2fb8u9m59vzb3tk/step3.png?dl=1) 10 | 11 | ## Build Phases ➜ Link Binary With Libraries 12 | 13 | - Select your project in the **`Project navigator`**. Click **`Build Phases`** then **`Link Binary With Libraries`**. Add the following static library: 14 | - **`libRNBackgroundFetch.a`**. 15 | ![](https://dl.dropboxusercontent.com/s/2977uvmdpavv4fn/step4.png?dl=1) 16 | 17 | - BackgroundGeolocation includes custom iOS framework. This needs to be added manually, unfortunately. 18 | - Click **`[Add Other...]`**. 19 | - Navigate: **`node_modules/react-native-background-fetch/ios/RNBackgroundFetch`**. 20 | - Add **`TSBackgroundFetch.framework`**. 21 | ![](https://dl.dropboxusercontent.com/s/bjzlgfa34rnev1v/step5.png?dl=1) 22 | 23 | ## Build Settings ➜ Framework Search Paths 24 | 25 | - In order to the find the **`TSBackgroundFetch.framework`** you just added, you have to tell Xcode where it can find it: 26 | - Go to **Build Settings** and search for **"framework search path"**. 27 | - Add the following paths (select **recursive [v]**): 28 | 29 | ``` 30 | $(PROJECT_DIR)/../node_modules/react-native-background-fetch/ios 31 | ``` 32 | 33 | ![](https://dl.dropboxusercontent.com/s/4smmcpk021gl10u/step8.png?dl=1) 34 | 35 | ## Configure Background Capabilities 36 | 37 | - Select the root of your project. Select **Capabilities** tab. Enable **Background Modes** and enable the following mode: 38 | 39 | - [x] Background fetch 40 | 41 | ![](https://dl.dropboxusercontent.com/s/9f86qcx6l4v1muj/step6.png?dl=1) 42 | 43 | ## BackgroundFetch AppDelegate extension 44 | 45 | BackgroundFetch implements an `AppDelegate` method `didPerformFetchWithCompletionHandler`. You must manually add this file to the same folder where your `AppDelegate.m` lives: 46 | 47 | - Expand the **`RNBackgroundFetch`** project and drag/drop the file **`RNBackgroundFetch+AppDelegate.m`** and place the file to exist **in the same folder** as your app's **`AppDelegate.m`**. 48 | ![](https://dl.dropboxusercontent.com/s/2n614ns4w8hbf30/step7.png?dl=1) 49 | 50 | You can now [import and build](../README.md#example) 51 | -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/.editorconfig: -------------------------------------------------------------------------------- 1 | # Windows files 2 | [*.bat] 3 | end_of_line = crlf 4 | -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | }; 5 | -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | # Windows files should use crlf line endings 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | *.bat text eol=crlf 4 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | ios/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | 35 | # node.js 36 | # 37 | node_modules/ 38 | npm-debug.log 39 | yarn-error.log 40 | 41 | # BUCK 42 | buck-out/ 43 | \.buckd/ 44 | *.keystore 45 | !debug.keystore 46 | 47 | # fastlane 48 | # 49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 50 | # screenshots whenever they are needed. 51 | # For more information about the recommended setup visit: 52 | # https://docs.fastlane.tools/best-practices/source-control/ 53 | 54 | */fastlane/report.xml 55 | */fastlane/Preview.html 56 | */fastlane/screenshots 57 | **/fastlane/test_output 58 | 59 | # Bundle artifact 60 | *.jsbundle 61 | 62 | # Ruby / CocoaPods 63 | /ios/Pods/ 64 | /vendor/bundle/ 65 | -------------------------------------------------------------------------------- /example/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | arrowParens: 'avoid', 5 | bracketSameLine: true, 6 | bracketSpacing: false, 7 | singleQuote: true, 8 | trailingComma: 'all', 9 | arrowParens: 'avoid', 10 | }; 11 | -------------------------------------------------------------------------------- /example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.5 2 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/App.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * 5 | * Generated with the TypeScript template 6 | * https://github.com/react-native-community/react-native-template-typescript 7 | * 8 | * @format 9 | */ 10 | 11 | import React from 'react'; 12 | import { 13 | SafeAreaView, 14 | ScrollView, 15 | StatusBar, 16 | StyleSheet, 17 | Text, 18 | View, 19 | Switch, 20 | Button, 21 | Alert 22 | } from 'react-native'; 23 | 24 | import BackgroundFetch from "react-native-background-fetch"; 25 | 26 | const Colors = { 27 | gold: '#fedd1e', 28 | black: '#000', 29 | white: '#fff', 30 | lightGrey: '#ccc', 31 | blue: '#337AB7', 32 | } 33 | 34 | /// Util class for handling fetch-event peristence in AsyncStorage. 35 | import Event from "./src/Event"; 36 | 37 | const App = () => { 38 | 39 | const [enabled, setEnabled] = React.useState(false); 40 | const [status, setStatus] = React.useState(-1); 41 | const [events, setEvents] = React.useState([]); 42 | 43 | React.useEffect(() => { 44 | initBackgroundFetch() 45 | loadEvents(); 46 | }, []); 47 | 48 | /// Configure BackgroundFetch. 49 | /// 50 | const initBackgroundFetch = async () => { 51 | const status:number = await BackgroundFetch.configure({ 52 | minimumFetchInterval: 15, // <-- minutes (15 is minimum allowed) 53 | stopOnTerminate: false, 54 | enableHeadless: true, 55 | startOnBoot: true, 56 | // Android options 57 | forceAlarmManager: false, // <-- Set true to bypass JobScheduler. 58 | requiredNetworkType: BackgroundFetch.NETWORK_TYPE_NONE, // Default 59 | requiresCharging: false, // Default 60 | requiresDeviceIdle: false, // Default 61 | requiresBatteryNotLow: false, // Default 62 | requiresStorageNotLow: false, // Default 63 | }, async (taskId:string) => { 64 | console.log('[BackgroundFetch] taskId', taskId); 65 | // Create an Event record. 66 | const event = await Event.create(taskId, false); 67 | // Update state. 68 | setEvents((prev) => [...prev, event]); 69 | // Finish. 70 | BackgroundFetch.finish(taskId); 71 | }, (taskId:string) => { 72 | // Oh No! Our task took too long to complete and the OS has signalled 73 | // that this task must be finished immediately. 74 | console.log('[Fetch] TIMEOUT taskId:', taskId); 75 | BackgroundFetch.finish(taskId); 76 | }); 77 | setStatus(status); 78 | setEnabled(true); 79 | } 80 | 81 | /// Load persisted events from AsyncStorage. 82 | /// 83 | const loadEvents = () => { 84 | Event.all().then((data) => { 85 | setEvents(data); 86 | }).catch((error) => { 87 | Alert.alert('Error', 'Failed to load data from AsyncStorage: ' + error); 88 | }); 89 | } 90 | 91 | /// Toggle BackgroundFetch ON/OFF 92 | /// 93 | const onClickToggleEnabled = (value:boolean) => { 94 | setEnabled(value); 95 | 96 | if (value) { 97 | BackgroundFetch.start(); 98 | } else { 99 | BackgroundFetch.stop(); 100 | } 101 | } 102 | 103 | /// [Status] button handler. 104 | /// 105 | const onClickStatus = () => { 106 | BackgroundFetch.status().then((status:number) => { 107 | let statusConst = ''; 108 | switch (status) { 109 | case BackgroundFetch.STATUS_AVAILABLE: 110 | statusConst = 'STATUS_AVAILABLE'; 111 | break; 112 | case BackgroundFetch.STATUS_DENIED: 113 | statusConst = 'STATUS_DENIED'; 114 | break; 115 | case BackgroundFetch.STATUS_RESTRICTED: 116 | statusConst = 'STATUS_RESTRICTED'; 117 | break; 118 | } 119 | Alert.alert('BackgroundFetch.status()', `${statusConst} (${status})`); 120 | }); 121 | } 122 | 123 | /// [scheduleTask] button handler. 124 | /// Schedules a custom-task to fire in 5000ms 125 | /// 126 | const onClickScheduleTask = () => { 127 | BackgroundFetch.scheduleTask({ 128 | taskId: 'com.transistorsoft.customtask', 129 | delay: 5000, 130 | forceAlarmManager: true 131 | }).then(() => { 132 | Alert.alert('scheduleTask', 'Scheduled task with delay: 5000ms'); 133 | }).catch((error) => { 134 | Alert.alert('scheduleTask ERROR', error); 135 | }); 136 | } 137 | 138 | /// Clear the Events list. 139 | /// 140 | const onClickClear = () => { 141 | Event.destroyAll(); 142 | setEvents([]); 143 | } 144 | 145 | /// Fetch events renderer. 146 | /// 147 | const renderEvents = () => { 148 | if (!events.length) { 149 | return ( 150 | Waiting for BackgroundFetch events... 151 | ); 152 | } 153 | return events.slice().reverse().map(event => ( 154 | 155 | 156 | {event.taskId} {event.isHeadless ? '[Headless]' : ''} 157 | 158 | {event.timestamp} 159 | 160 | )) 161 | } 162 | 163 | return ( 164 | 165 | 166 | 167 | 168 | 169 | BGFetch Demo 170 | 171 | 172 | 175 | {renderEvents()} 176 | 177 | 178 |