├── .circleci └── config.yml ├── .eslintignore ├── .eslintrc.js ├── .github └── dependabot.yml ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── ocetnik │ └── timer │ ├── BackgroundTimerModule.java │ └── BackgroundTimerPackage.java ├── index.js ├── ios ├── RNBackgroundTimer.h ├── RNBackgroundTimer.m └── RNBackgroundTimer.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ └── jlexyc.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── package-lock.json ├── package.json ├── react-native-background-timer.podspec └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:current 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: yarn install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run eslint 37 | - run: yarn eslint 38 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['airbnb', 'prettier'], 3 | plugins: ['import', 'jsx-a11y', 'prettier', 'react', 'react-hooks'], 4 | rules: { 5 | 'class-methods-use-this': ['error', { exceptMethods: ['start', 'stop'] }], 6 | 'prettier/prettier': ['error'], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | versioning-strategy: increase 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | .idea 4 | node_modules 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dávid Ocetník 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Background Timer 2 | Emit event periodically (even when app is in the background). 3 | 4 | ## Installation 5 | 1. If you use Expo to create a project [you'll just need to](https://facebook.github.io/react-native/docs/getting-started#caveats) "[eject](https://docs.expo.io/versions/latest/expokit/eject)". 6 | 7 | ```bash 8 | expo eject 9 | ``` 10 | 11 | 2. Install React Native Background Timer package. 12 | 13 | ```bash 14 | yarn add react-native-background-timer 15 | # or using npm 16 | npm install react-native-background-timer --save 17 | ``` 18 | 19 | 3. Link React Native Background Timer library. This step is not necessary when you use React Native >= 0.60 (and your app is not ejected from Expo). 20 | 21 | ```bash 22 | react-native link react-native-background-timer 23 | ``` 24 | 25 | 4. If you use CocoaPods or React Native >= 0.60 (and your app is not ejected from Expo) or your app is ejected from Expo, then before running your app on iOS, make sure you have CocoaPods installed and run: 26 | 27 | ```bash 28 | cd ios 29 | pod install 30 | ``` 31 | 32 | Link the library manually if you get errors: 33 | 34 | - Android: `TypeError: Cannot read property 'setTimeout' of undefined` or `TypeError: null is not an object (evaluating 'RNBackgroundTimer.setTimeout')` 35 | - iOS: `Native module cannot be null` 36 | 37 |
38 | Android manual linking 39 | 40 | - `android/settings.gradle` 41 | 42 | ```diff 43 | + include ':react-native-background-timer' 44 | + project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android') 45 | ``` 46 | 47 | - `android/app/build.gradle` 48 | 49 | ```diff 50 | dependencies { 51 | + implementation project(':react-native-background-timer') 52 | } 53 | ``` 54 | 55 | - `android/app/src/main/java/com/your-app/MainApplication.java` 56 | 57 | ```diff 58 | + import com.ocetnik.timer.BackgroundTimerPackage; 59 | 60 | @Override 61 | protected List getPackages() { 62 | return Arrays.asList( 63 | + new BackgroundTimerPackage() 64 | ); 65 | } 66 | ``` 67 |
68 | 69 |
70 | iOS manual linking 71 | 72 | - `ios/Podfile` 73 | 74 | ```diff 75 | + pod 'react-native-background-timer', :path => '../node_modules/react-native-background-timer' 76 | ``` 77 |
78 | 79 | ## Usage 80 | 81 | ```js 82 | import BackgroundTimer from 'react-native-background-timer'; 83 | ``` 84 | 85 | ### Crossplatform 86 | To use the same code both on Android and iOS use runBackgroundTimer() and stopBackgroundTimer(). There can be used only one background timer to keep code consistent. 87 | 88 | ```js 89 | BackgroundTimer.runBackgroundTimer(() => { 90 | //code that will be called every 3 seconds 91 | }, 92 | 3000); 93 | //rest of code will be performing for iOS on background too 94 | 95 | BackgroundTimer.stopBackgroundTimer(); //after this call all code on background stop run. 96 | ``` 97 | 98 | ### iOS 99 | After iOS update logic of background task little bit changed. So we can't use as it was. 100 | You have to use only start() and stop() without parameters. And all code that is performing will continue performing on background including all setTimeout() timers. 101 | 102 | Example: 103 | ```js 104 | BackgroundTimer.start(); 105 | // Do whatever you want incuding setTimeout; 106 | BackgroundTimer.stop(); 107 | ``` 108 | 109 | > If you call stop() on background no new tasks will be started! 110 | > Don't call .start() twice, as it stop performing previous background task and starts new. 111 | > If it will be called on backgound no tasks will run. 112 | 113 | ### Android 114 | You can use the `setInterval` and `setTimeout` functions. 115 | This API is identical to that of `react-native` and can be used to quickly replace existing timers 116 | with background timers. 117 | 118 | ```js 119 | // Start a timer that runs continuous after X milliseconds 120 | const intervalId = BackgroundTimer.setInterval(() => { 121 | // this will be executed every 200 ms 122 | // even when app is the the background 123 | console.log('tic'); 124 | }, 200); 125 | 126 | // Cancel the timer when you are done with it 127 | BackgroundTimer.clearInterval(intervalId); 128 | ``` 129 | 130 | ```js 131 | // Start a timer that runs once after X milliseconds 132 | const timeoutId = BackgroundTimer.setTimeout(() => { 133 | // this will be executed once after 10 seconds 134 | // even when app is the the background 135 | console.log('tac'); 136 | }, 10000); 137 | 138 | // Cancel the timeout if necessary 139 | BackgroundTimer.clearTimeout(timeoutId); 140 | ``` 141 | 142 | ### Obsolete 143 | Obsolete usage which doesn't support multiple background timers. 144 | 145 | ```js 146 | import { 147 | DeviceEventEmitter, 148 | NativeAppEventEmitter, 149 | Platform, 150 | } from 'react-native'; 151 | 152 | import BackgroundTimer from 'react-native-background-timer'; 153 | ``` 154 | 155 | ```js 156 | const EventEmitter = Platform.select({ 157 | ios: () => NativeAppEventEmitter, 158 | android: () => DeviceEventEmitter, 159 | })(); 160 | ``` 161 | 162 | ```js 163 | // start a global timer 164 | BackgroundTimer.start(5000); // delay in milliseconds only for Android 165 | ``` 166 | ```js 167 | // listen for event 168 | EventEmitter.addListener('backgroundTimer', () => { 169 | // this will be executed once after 5 seconds 170 | console.log('toe'); 171 | }); 172 | ``` 173 | ```js 174 | // stop the timer 175 | BackgroundTimer.stop(); 176 | ``` 177 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def safeExtGet(prop, fallback) { 4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 5 | } 6 | 7 | android { 8 | compileSdkVersion safeExtGet('compileSdkVersion', 28) 9 | 10 | defaultConfig { 11 | minSdkVersion safeExtGet('minSdkVersion', 16) 12 | targetSdkVersion safeExtGet('targetSdkVersion', 28) 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | } 17 | 18 | dependencies { 19 | implementation 'com.facebook.react:react-native:+' 20 | } 21 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/java/com/ocetnik/timer/BackgroundTimerModule.java: -------------------------------------------------------------------------------- 1 | package com.ocetnik.timer; 2 | 3 | import android.os.Handler; 4 | import android.os.PowerManager; 5 | 6 | import com.facebook.react.bridge.LifecycleEventListener; 7 | import com.facebook.react.bridge.ReactApplicationContext; 8 | import com.facebook.react.bridge.ReactContext; 9 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 10 | import com.facebook.react.bridge.ReactMethod; 11 | import com.facebook.react.modules.core.DeviceEventManagerModule; 12 | 13 | import java.lang.Runnable; 14 | 15 | public class BackgroundTimerModule extends ReactContextBaseJavaModule { 16 | 17 | private Handler handler; 18 | private ReactContext reactContext; 19 | private Runnable runnable; 20 | private PowerManager powerManager; 21 | private PowerManager.WakeLock wakeLock; 22 | private final LifecycleEventListener listener = new LifecycleEventListener(){ 23 | @Override 24 | public void onHostResume() {} 25 | 26 | @Override 27 | public void onHostPause() {} 28 | 29 | @Override 30 | public void onHostDestroy() { 31 | if (wakeLock.isHeld()) wakeLock.release(); 32 | } 33 | }; 34 | 35 | public BackgroundTimerModule(ReactApplicationContext reactContext) { 36 | super(reactContext); 37 | this.reactContext = reactContext; 38 | this.powerManager = (PowerManager) getReactApplicationContext().getSystemService(reactContext.POWER_SERVICE); 39 | this.wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "rohit_bg_wakelock"); 40 | reactContext.addLifecycleEventListener(listener); 41 | } 42 | 43 | @Override 44 | public String getName() { 45 | return "RNBackgroundTimer"; 46 | } 47 | 48 | @ReactMethod 49 | public void start(final int delay) { 50 | if (!wakeLock.isHeld()) wakeLock.acquire(); 51 | 52 | handler = new Handler(); 53 | runnable = new Runnable() { 54 | @Override 55 | public void run() { 56 | sendEvent(reactContext, "backgroundTimer"); 57 | } 58 | }; 59 | 60 | handler.post(runnable); 61 | } 62 | 63 | @ReactMethod 64 | public void stop() { 65 | if (wakeLock.isHeld()) wakeLock.release(); 66 | 67 | // avoid null pointer exceptio when stop is called without start 68 | if (handler != null) handler.removeCallbacks(runnable); 69 | } 70 | 71 | private void sendEvent(ReactContext reactContext, String eventName) { 72 | reactContext 73 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 74 | .emit(eventName, null); 75 | } 76 | 77 | @ReactMethod 78 | public void setTimeout(final int id, final double timeout) { 79 | Handler handler = new Handler(); 80 | handler.postDelayed(new Runnable(){ 81 | @Override 82 | public void run(){ 83 | if (getReactApplicationContext().hasActiveCatalystInstance()) { 84 | getReactApplicationContext() 85 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 86 | .emit("backgroundTimer.timeout", id); 87 | } 88 | } 89 | }, (long) timeout); 90 | } 91 | 92 | /*@ReactMethod 93 | public void clearTimeout(final int id) { 94 | // todo one day.. 95 | // not really neccessary to have 96 | }*/ 97 | } 98 | -------------------------------------------------------------------------------- /android/src/main/java/com/ocetnik/timer/BackgroundTimerPackage.java: -------------------------------------------------------------------------------- 1 | package com.ocetnik.timer; 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 BackgroundTimerPackage implements ReactPackage { 14 | 15 | public BackgroundTimerPackage() { 16 | } 17 | 18 | @Override 19 | public List createNativeModules(ReactApplicationContext reactContext) { 20 | List modules = new ArrayList<>(); 21 | modules.add(new BackgroundTimerModule(reactContext)); 22 | 23 | return modules; 24 | } 25 | 26 | public List> createJSModules() { 27 | return Collections.emptyList(); 28 | } 29 | 30 | @Override 31 | public List createViewManagers(ReactApplicationContext reactContext) { 32 | return Collections.emptyList(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { 2 | DeviceEventEmitter, 3 | NativeAppEventEmitter, 4 | NativeEventEmitter, 5 | NativeModules, 6 | Platform, 7 | } from 'react-native'; 8 | 9 | const { RNBackgroundTimer } = NativeModules; 10 | const Emitter = new NativeEventEmitter(RNBackgroundTimer); 11 | 12 | class BackgroundTimer { 13 | constructor() { 14 | this.uniqueId = 0; 15 | this.callbacks = {}; 16 | 17 | Emitter.addListener('backgroundTimer.timeout', (id) => { 18 | if (this.callbacks[id]) { 19 | const callbackById = this.callbacks[id]; 20 | const { callback } = callbackById; 21 | if (!this.callbacks[id].interval) { 22 | delete this.callbacks[id]; 23 | } else { 24 | RNBackgroundTimer.setTimeout(id, this.callbacks[id].timeout); 25 | } 26 | callback(); 27 | } 28 | }); 29 | } 30 | 31 | // Original API 32 | start(delay = 0) { 33 | return RNBackgroundTimer.start(delay); 34 | } 35 | 36 | stop() { 37 | return RNBackgroundTimer.stop(); 38 | } 39 | 40 | runBackgroundTimer(callback, delay) { 41 | const EventEmitter = Platform.select({ 42 | ios: () => NativeAppEventEmitter, 43 | android: () => DeviceEventEmitter, 44 | })(); 45 | this.start(0); 46 | this.backgroundListener = EventEmitter.addListener( 47 | 'backgroundTimer', 48 | () => { 49 | this.backgroundListener.remove(); 50 | this.backgroundClockMethod(callback, delay); 51 | }, 52 | ); 53 | } 54 | 55 | backgroundClockMethod(callback, delay) { 56 | this.backgroundTimer = this.setTimeout(() => { 57 | callback(); 58 | this.backgroundClockMethod(callback, delay); 59 | }, delay); 60 | } 61 | 62 | stopBackgroundTimer() { 63 | this.stop(); 64 | this.clearTimeout(this.backgroundTimer); 65 | } 66 | 67 | // New API, allowing for multiple timers 68 | setTimeout(callback, timeout) { 69 | this.uniqueId += 1; 70 | const timeoutId = this.uniqueId; 71 | this.callbacks[timeoutId] = { 72 | callback, 73 | interval: false, 74 | timeout, 75 | }; 76 | RNBackgroundTimer.setTimeout(timeoutId, timeout); 77 | return timeoutId; 78 | } 79 | 80 | clearTimeout(timeoutId) { 81 | if (this.callbacks[timeoutId]) { 82 | delete this.callbacks[timeoutId]; 83 | // RNBackgroundTimer.clearTimeout(timeoutId); 84 | } 85 | } 86 | 87 | setInterval(callback, timeout) { 88 | this.uniqueId += 1; 89 | const intervalId = this.uniqueId; 90 | this.callbacks[intervalId] = { 91 | callback, 92 | interval: true, 93 | timeout, 94 | }; 95 | RNBackgroundTimer.setTimeout(intervalId, timeout); 96 | return intervalId; 97 | } 98 | 99 | clearInterval(intervalId) { 100 | if (this.callbacks[intervalId]) { 101 | delete this.callbacks[intervalId]; 102 | // RNBackgroundTimer.clearTimeout(intervalId); 103 | } 104 | } 105 | } 106 | 107 | export default new BackgroundTimer(); 108 | -------------------------------------------------------------------------------- /ios/RNBackgroundTimer.h: -------------------------------------------------------------------------------- 1 | // 2 | // RNBackgroundTimer.h 3 | // react-native-background-timer 4 | // 5 | // Created by IjzerenHein on 06-09-2016. 6 | // Copyright (c) ATO Gear. All rights reserved. 7 | // 8 | 9 | #import 10 | // Support React Native headers both in the React namespace, where they are in RN version 0.40+, 11 | // and no namespace, for older versions of React Native 12 | #if __has_include() 13 | #import 14 | #else 15 | #import "RCTEventEmitter.h" 16 | #endif 17 | 18 | 19 | @interface RNBackgroundTimer : RCTEventEmitter 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /ios/RNBackgroundTimer.m: -------------------------------------------------------------------------------- 1 | // 2 | // RNBackgroundTimer.m 3 | // react-native-background-timer 4 | // 5 | // Created by IjzerenHein on 06-09-2016. 6 | // Copyright (c) ATO Gear. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "RNBackgroundTimer.h" 11 | 12 | @implementation RNBackgroundTimer { 13 | UIBackgroundTaskIdentifier bgTask; 14 | int delay; 15 | } 16 | 17 | RCT_EXPORT_MODULE() 18 | 19 | - (NSArray *)supportedEvents { return @[@"backgroundTimer", @"backgroundTimer.timeout"]; } 20 | 21 | - (void) _start 22 | { 23 | [self _stop]; 24 | bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"RNBackgroundTimer" expirationHandler:^{ 25 | // Clean up any unfinished task business by marking where you 26 | // stopped or ending the task outright. 27 | [[UIApplication sharedApplication] endBackgroundTask:bgTask]; 28 | bgTask = UIBackgroundTaskInvalid; 29 | }]; 30 | 31 | UIBackgroundTaskIdentifier thisBgTask = bgTask; 32 | dispatch_async(dispatch_get_main_queue(), ^{ 33 | if ([self bridge] != nil && thisBgTask == bgTask) { 34 | [self sendEventWithName:@"backgroundTimer" body:[NSNumber numberWithInt:(int)thisBgTask]]; 35 | } 36 | }); 37 | } 38 | 39 | - (void) _stop 40 | { 41 | if (bgTask != UIBackgroundTaskInvalid) { 42 | [[UIApplication sharedApplication] endBackgroundTask:bgTask]; 43 | bgTask = UIBackgroundTaskInvalid; 44 | } 45 | } 46 | 47 | RCT_EXPORT_METHOD(start:(double)_delay 48 | resolver:(RCTPromiseResolveBlock)resolve 49 | rejecter:(RCTPromiseRejectBlock)reject) 50 | { 51 | delay = _delay; 52 | [self _start]; 53 | resolve([NSNumber numberWithBool:YES]); 54 | } 55 | 56 | RCT_EXPORT_METHOD(stop:(RCTPromiseResolveBlock)resolve 57 | rejecter:(RCTPromiseRejectBlock)reject) 58 | { 59 | [self _stop]; 60 | resolve([NSNumber numberWithBool:YES]); 61 | } 62 | 63 | RCT_EXPORT_METHOD(setTimeout:(int)timeoutId 64 | timeout:(double)timeout 65 | resolver:(RCTPromiseResolveBlock)resolve 66 | rejecter:(RCTPromiseRejectBlock)reject) 67 | { 68 | __block UIBackgroundTaskIdentifier task = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"RNBackgroundTimer" expirationHandler:^{ 69 | [[UIApplication sharedApplication] endBackgroundTask:task]; 70 | }]; 71 | 72 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ 73 | if ([self bridge] != nil) { 74 | [self sendEventWithName:@"backgroundTimer.timeout" body:[NSNumber numberWithInt:timeoutId]]; 75 | } 76 | [[UIApplication sharedApplication] endBackgroundTask:task]; 77 | }); 78 | resolve([NSNumber numberWithBool:YES]); 79 | } 80 | 81 | /* 82 | RCT_EXPORT_METHOD(clearTimeout:(int)timeoutId 83 | resolver:(RCTPromiseResolveBlock)resolve 84 | rejecter:(RCTPromiseRejectBlock)reject) 85 | { 86 | // Do nothing :) 87 | // timeout will be ignored in javascript anyway :) 88 | }*/ 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /ios/RNBackgroundTimer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6419269B1ECB4266009CF731 /* RNBackgroundTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNBackgroundTimer.m */; }; 11 | B3E7B58A1CC2AC0600A0062D /* RNBackgroundTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNBackgroundTimer.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 58B511D91A9E6C8500147676 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 0; 23 | }; 24 | 641926901ECB4257009CF731 /* CopyFiles */ = { 25 | isa = PBXCopyFilesBuildPhase; 26 | buildActionMask = 2147483647; 27 | dstPath = "include/$(PRODUCT_NAME)"; 28 | dstSubfolderSpec = 16; 29 | files = ( 30 | ); 31 | runOnlyForDeploymentPostprocessing = 0; 32 | }; 33 | /* End PBXCopyFilesBuildPhase section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 134814201AA4EA6300B7C361 /* libRNBackgroundTimer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNBackgroundTimer.a; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 641926921ECB4257009CF731 /* libRNBackgroundTimer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNBackgroundTimer.a; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | B3E7B5881CC2AC0600A0062D /* RNBackgroundTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBackgroundTimer.h; sourceTree = ""; }; 39 | B3E7B5891CC2AC0600A0062D /* RNBackgroundTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNBackgroundTimer.m; sourceTree = ""; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | 6419268F1ECB4257009CF731 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | 134814211AA4EA7D00B7C361 /* Products */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 134814201AA4EA6300B7C361 /* libRNBackgroundTimer.a */, 64 | ); 65 | name = Products; 66 | sourceTree = ""; 67 | }; 68 | 58B511D21A9E6C8500147676 = { 69 | isa = PBXGroup; 70 | children = ( 71 | B3E7B5881CC2AC0600A0062D /* RNBackgroundTimer.h */, 72 | B3E7B5891CC2AC0600A0062D /* RNBackgroundTimer.m */, 73 | 134814211AA4EA7D00B7C361 /* Products */, 74 | 641926921ECB4257009CF731 /* libRNBackgroundTimer.a */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | /* End PBXGroup section */ 79 | 80 | /* Begin PBXNativeTarget section */ 81 | 58B511DA1A9E6C8500147676 /* RNBackgroundTimer */ = { 82 | isa = PBXNativeTarget; 83 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNBackgroundTimer" */; 84 | buildPhases = ( 85 | 58B511D71A9E6C8500147676 /* Sources */, 86 | 58B511D81A9E6C8500147676 /* Frameworks */, 87 | 58B511D91A9E6C8500147676 /* CopyFiles */, 88 | ); 89 | buildRules = ( 90 | ); 91 | dependencies = ( 92 | ); 93 | name = RNBackgroundTimer; 94 | productName = RCTDataManager; 95 | productReference = 134814201AA4EA6300B7C361 /* libRNBackgroundTimer.a */; 96 | productType = "com.apple.product-type.library.static"; 97 | }; 98 | 641926911ECB4257009CF731 /* RNBackgroundTimer-tvOS */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = 6419269A1ECB4257009CF731 /* Build configuration list for PBXNativeTarget "RNBackgroundTimer-tvOS" */; 101 | buildPhases = ( 102 | 6419268E1ECB4257009CF731 /* Sources */, 103 | 6419268F1ECB4257009CF731 /* Frameworks */, 104 | 641926901ECB4257009CF731 /* CopyFiles */, 105 | ); 106 | buildRules = ( 107 | ); 108 | dependencies = ( 109 | ); 110 | name = "RNBackgroundTimer-tvOS"; 111 | productName = "RNBackgroundTimer-tvOS"; 112 | productReference = 641926921ECB4257009CF731 /* libRNBackgroundTimer.a */; 113 | productType = "com.apple.product-type.library.static"; 114 | }; 115 | /* End PBXNativeTarget section */ 116 | 117 | /* Begin PBXProject section */ 118 | 58B511D31A9E6C8500147676 /* Project object */ = { 119 | isa = PBXProject; 120 | attributes = { 121 | LastUpgradeCheck = 0610; 122 | ORGANIZATIONNAME = Facebook; 123 | TargetAttributes = { 124 | 58B511DA1A9E6C8500147676 = { 125 | CreatedOnToolsVersion = 6.1.1; 126 | }; 127 | 641926911ECB4257009CF731 = { 128 | CreatedOnToolsVersion = 8.3.2; 129 | ProvisioningStyle = Automatic; 130 | }; 131 | }; 132 | }; 133 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNBackgroundTimer" */; 134 | compatibilityVersion = "Xcode 3.2"; 135 | developmentRegion = English; 136 | hasScannedForEncodings = 0; 137 | knownRegions = ( 138 | en, 139 | ); 140 | mainGroup = 58B511D21A9E6C8500147676; 141 | productRefGroup = 58B511D21A9E6C8500147676; 142 | projectDirPath = ""; 143 | projectRoot = ""; 144 | targets = ( 145 | 58B511DA1A9E6C8500147676 /* RNBackgroundTimer */, 146 | 641926911ECB4257009CF731 /* RNBackgroundTimer-tvOS */, 147 | ); 148 | }; 149 | /* End PBXProject section */ 150 | 151 | /* Begin PBXSourcesBuildPhase section */ 152 | 58B511D71A9E6C8500147676 /* Sources */ = { 153 | isa = PBXSourcesBuildPhase; 154 | buildActionMask = 2147483647; 155 | files = ( 156 | B3E7B58A1CC2AC0600A0062D /* RNBackgroundTimer.m in Sources */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | 6419268E1ECB4257009CF731 /* Sources */ = { 161 | isa = PBXSourcesBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | 6419269B1ECB4266009CF731 /* RNBackgroundTimer.m in Sources */, 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | /* End PBXSourcesBuildPhase section */ 169 | 170 | /* Begin XCBuildConfiguration section */ 171 | 58B511ED1A9E6C8500147676 /* Debug */ = { 172 | isa = XCBuildConfiguration; 173 | buildSettings = { 174 | ALWAYS_SEARCH_USER_PATHS = NO; 175 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 176 | CLANG_CXX_LIBRARY = "libc++"; 177 | CLANG_ENABLE_MODULES = YES; 178 | CLANG_ENABLE_OBJC_ARC = YES; 179 | CLANG_WARN_BOOL_CONVERSION = YES; 180 | CLANG_WARN_CONSTANT_CONVERSION = YES; 181 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 182 | CLANG_WARN_EMPTY_BODY = YES; 183 | CLANG_WARN_ENUM_CONVERSION = YES; 184 | CLANG_WARN_INT_CONVERSION = YES; 185 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 186 | CLANG_WARN_UNREACHABLE_CODE = YES; 187 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 188 | COPY_PHASE_STRIP = NO; 189 | ENABLE_STRICT_OBJC_MSGSEND = YES; 190 | GCC_C_LANGUAGE_STANDARD = gnu99; 191 | GCC_DYNAMIC_NO_PIC = NO; 192 | GCC_OPTIMIZATION_LEVEL = 0; 193 | GCC_PREPROCESSOR_DEFINITIONS = ( 194 | "DEBUG=1", 195 | "$(inherited)", 196 | ); 197 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 198 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 199 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 200 | GCC_WARN_UNDECLARED_SELECTOR = YES; 201 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 202 | GCC_WARN_UNUSED_FUNCTION = YES; 203 | GCC_WARN_UNUSED_VARIABLE = YES; 204 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 205 | MTL_ENABLE_DEBUG_INFO = YES; 206 | ONLY_ACTIVE_ARCH = YES; 207 | SDKROOT = iphoneos; 208 | }; 209 | name = Debug; 210 | }; 211 | 58B511EE1A9E6C8500147676 /* Release */ = { 212 | isa = XCBuildConfiguration; 213 | buildSettings = { 214 | ALWAYS_SEARCH_USER_PATHS = NO; 215 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 216 | CLANG_CXX_LIBRARY = "libc++"; 217 | CLANG_ENABLE_MODULES = YES; 218 | CLANG_ENABLE_OBJC_ARC = YES; 219 | CLANG_WARN_BOOL_CONVERSION = YES; 220 | CLANG_WARN_CONSTANT_CONVERSION = YES; 221 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 222 | CLANG_WARN_EMPTY_BODY = YES; 223 | CLANG_WARN_ENUM_CONVERSION = YES; 224 | CLANG_WARN_INT_CONVERSION = YES; 225 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 226 | CLANG_WARN_UNREACHABLE_CODE = YES; 227 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 228 | COPY_PHASE_STRIP = YES; 229 | ENABLE_NS_ASSERTIONS = NO; 230 | ENABLE_STRICT_OBJC_MSGSEND = YES; 231 | GCC_C_LANGUAGE_STANDARD = gnu99; 232 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 233 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 234 | GCC_WARN_UNDECLARED_SELECTOR = YES; 235 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 236 | GCC_WARN_UNUSED_FUNCTION = YES; 237 | GCC_WARN_UNUSED_VARIABLE = YES; 238 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 239 | MTL_ENABLE_DEBUG_INFO = NO; 240 | SDKROOT = iphoneos; 241 | VALIDATE_PRODUCT = YES; 242 | }; 243 | name = Release; 244 | }; 245 | 58B511F01A9E6C8500147676 /* Debug */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | HEADER_SEARCH_PATHS = ( 249 | "$(inherited)", 250 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 251 | "$(SRCROOT)/../../../React/**", 252 | "$(SRCROOT)/../../react-native/React/**", 253 | ); 254 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 255 | OTHER_LDFLAGS = "-ObjC"; 256 | PRODUCT_NAME = RNBackgroundTimer; 257 | SKIP_INSTALL = YES; 258 | }; 259 | name = Debug; 260 | }; 261 | 58B511F11A9E6C8500147676 /* Release */ = { 262 | isa = XCBuildConfiguration; 263 | buildSettings = { 264 | HEADER_SEARCH_PATHS = ( 265 | "$(inherited)", 266 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 267 | "$(SRCROOT)/../../../React/**", 268 | "$(SRCROOT)/../../react-native/React/**", 269 | ); 270 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 271 | OTHER_LDFLAGS = "-ObjC"; 272 | PRODUCT_NAME = RNBackgroundTimer; 273 | SKIP_INSTALL = YES; 274 | }; 275 | name = Release; 276 | }; 277 | 641926981ECB4257009CF731 /* Debug */ = { 278 | isa = XCBuildConfiguration; 279 | buildSettings = { 280 | CLANG_ANALYZER_NONNULL = YES; 281 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 282 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 283 | CLANG_WARN_INFINITE_RECURSION = YES; 284 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 285 | DEBUG_INFORMATION_FORMAT = dwarf; 286 | ENABLE_TESTABILITY = YES; 287 | GCC_NO_COMMON_BLOCKS = YES; 288 | OTHER_LDFLAGS = "-ObjC"; 289 | PRODUCT_NAME = RNBackgroundTimer; 290 | SDKROOT = appletvos; 291 | SKIP_INSTALL = YES; 292 | TVOS_DEPLOYMENT_TARGET = 10.2; 293 | }; 294 | name = Debug; 295 | }; 296 | 641926991ECB4257009CF731 /* Release */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | CLANG_ANALYZER_NONNULL = YES; 300 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 301 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 302 | CLANG_WARN_INFINITE_RECURSION = YES; 303 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 304 | COPY_PHASE_STRIP = NO; 305 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 306 | GCC_NO_COMMON_BLOCKS = YES; 307 | OTHER_LDFLAGS = "-ObjC"; 308 | PRODUCT_NAME = RNBackgroundTimer; 309 | SDKROOT = appletvos; 310 | SKIP_INSTALL = YES; 311 | TVOS_DEPLOYMENT_TARGET = 10.2; 312 | }; 313 | name = Release; 314 | }; 315 | /* End XCBuildConfiguration section */ 316 | 317 | /* Begin XCConfigurationList section */ 318 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNBackgroundTimer" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | 58B511ED1A9E6C8500147676 /* Debug */, 322 | 58B511EE1A9E6C8500147676 /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNBackgroundTimer" */ = { 328 | isa = XCConfigurationList; 329 | buildConfigurations = ( 330 | 58B511F01A9E6C8500147676 /* Debug */, 331 | 58B511F11A9E6C8500147676 /* Release */, 332 | ); 333 | defaultConfigurationIsVisible = 0; 334 | defaultConfigurationName = Release; 335 | }; 336 | 6419269A1ECB4257009CF731 /* Build configuration list for PBXNativeTarget "RNBackgroundTimer-tvOS" */ = { 337 | isa = XCConfigurationList; 338 | buildConfigurations = ( 339 | 641926981ECB4257009CF731 /* Debug */, 340 | 641926991ECB4257009CF731 /* Release */, 341 | ); 342 | defaultConfigurationIsVisible = 0; 343 | }; 344 | /* End XCConfigurationList section */ 345 | }; 346 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 347 | } 348 | -------------------------------------------------------------------------------- /ios/RNBackgroundTimer.xcodeproj/xcuserdata/jlexyc.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | RNBackgroundTimer-tvOS.xcscheme 8 | 9 | orderHint 10 | 42 11 | 12 | RNBackgroundTimer.xcscheme 13 | 14 | orderHint 15 | 41 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-background-timer", 3 | "version": "2.4.1", 4 | "description": "Emit event periodically (even when app is in the background)", 5 | "keywords": [ 6 | "react-native", 7 | "background", 8 | "timer", 9 | "android", 10 | "ios" 11 | ], 12 | "main": "index.js", 13 | "scripts": { 14 | "eslint": "eslint *.js", 15 | "eslint:fix": "yarn eslint --fix", 16 | "test": "echo \"Error: no test specified\" && exit 1" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/ocetnik/react-native-background-timer.git" 21 | }, 22 | "author": "David Ocetnik", 23 | "license": "MIT", 24 | "peerDependencies": { 25 | "react-native": ">=0.47.0" 26 | }, 27 | "devDependencies": { 28 | "@babel/core": "^7.21.3", 29 | "eslint": "^8.36.0", 30 | "eslint-config-airbnb": "^19.0.4", 31 | "eslint-config-prettier": "^8.6.0", 32 | "eslint-plugin-import": "^2.27.5", 33 | "eslint-plugin-jsx-a11y": "^6.7.1", 34 | "eslint-plugin-prettier": "^4.2.1", 35 | "eslint-plugin-react": "^7.32.2", 36 | "eslint-plugin-react-hooks": "^4.6.0", 37 | "husky": "^8.0.3", 38 | "lint-staged": "^13.1.2", 39 | "prettier": "^2.8.4", 40 | "react": "17.0.2", 41 | "react-native": "^0.68.6" 42 | }, 43 | "husky": { 44 | "hooks": { 45 | "pre-commit": "lint-staged" 46 | } 47 | }, 48 | "lint-staged": { 49 | "*.js": [ 50 | "eslint --fix", 51 | "git add" 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /react-native-background-timer.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | 7 | s.name = package['name'] 8 | s.version = package['version'] 9 | s.summary = package['description'] 10 | s.homepage = package['repository']['url'] 11 | s.license = package['license'] 12 | s.author = package['author'] 13 | s.source = { :git => s.homepage, :tag => 'v#{s.version}' } 14 | 15 | s.requires_arc = true 16 | s.ios.deployment_target = '8.0' 17 | s.tvos.deployment_target = '9.0' 18 | 19 | s.preserve_paths = 'README.md', 'package.json', 'index.js' 20 | s.source_files = 'ios/*.{h,m}' 21 | 22 | s.dependency 'React-Core' 23 | 24 | end 25 | --------------------------------------------------------------------------------