I've built a little iOS app in React Native that does location tracking, sending the lat/lng regularly to a server of the user's choosing. However this only works when the app is in the foreground. How can I run this task in the background when the user is in other apps?
Currently, there is, unfortunately, no support for background tasks of any kind. The feature you are referring to would be a background timer. Such a timer is this product pain (a feature request) for react native, you may upvote it to show an increased demand for this feature.
EDIT 12/2016: There is still no real option. You have the Headless JS API since RN 0.33, but it is only for android. Also your App will crash if this runs in the foreground so you have to be careful about using it. Thanks to #Feng for pointing that out.
And now there is react-native-background-task which is a single API for both Android and iOS.
I use this, and it seems to work: https://github.com/ocetnik/react-native-background-timer
Emit event periodically (even when app is in the background).
You can use the setInterval and setTimeout functions. This API is identical to that of react-native and can be used to quickly replace existing timers with background timers.
import BackgroundTimer from 'react-native-background-timer';
// Start a timer that runs continuous after X milliseconds
const intervalId = BackgroundTimer.setInterval(() => {
// this will be executed every 200 ms
// even when app is the background
console.log('tic');
}, 200);
// Cancel the timer when you are done with it
BackgroundTimer.clearInterval(intervalId);
// Start a timer that runs once after X milliseconds
const timeoutId = BackgroundTimer.setTimeout(() => {
// this will be executed once after 10 seconds
// even when app is the background
console.log('tac');
}, 10000);
// Cancel the timeout if necessary
BackgroundTimer.clearTimeout(timeoutId);
These libraries can help you to achieve your desired functionality:
react-native-background-job
react-native-background-task
react-native-background-fetch
Alternatively, you can use Headless JS from react-native. But its only available for Android.
The React Native ecosystem has been moving at breakneck pace over the past few months, and a few plugins have popped up to address the pain of not being able to run code in the background.
https://github.com/transistorsoft/react-native-background-fetch -- Wake up for 30 seconds periodically to run some arbitrary JS code. Not suitable for high resolution geolocation, as the time between wake-ups will be 15min or more.
https://github.com/transistorsoft/react-native-background-geolocation -- A better fit for this situation, targeted specifically at geolocation in the background.
With the use of endless task management system in android You can run the task in the background theater the app is in background or the app is in kill mode.
You can use native bridge to do that or You can use the package
Example Explaned here:
React-native package to do the same WithOut bridging react-native-endless-background-service-without-notification
We solved this my writing our own module in Android and iOS and used event to notify front end
Related
I have a problem implementing beacons in my app. I am using the library react-native-beacons-manager, but I think is a general "problem".
The issue is that when I kill my app (and this is important to reproduce the issue) and I get closer to my beacon, iOS fire an event regionDidEnter that is caught by a file wrote in native code, and then, sent to javascript using the method of RCTEventEmitter: [self sendEventWithName:#"regionDidEnter" body:event];
The problem is that this event is fired before javascript is fully loaded, so my listener:
// component.js
Beacons.BeaconsEventEmitter.addListener('regionDidEnter', b => {
//code
});
doesn't get called.
Order of events:
[BeaconsDemo] Did finish launching enter
[BeaconsDemo] Did finish launching After jsBundleURLForBundleRoot
[BeaconsDemo] Did finish launching After RCTRootView alloc
[BeaconsDemo] Did finish launching After UIWindow alloc
[BeaconsDemo] Did finish launching After makeKeyAndVisible
[BeaconsDemo] Did finish launching end
--iOS send the event and it is caught by RNiBeacon but it has no listeners yet--
[BeaconsDemo] no listeners in RnIBeacon.m
--Register
[BeaconsDemo] regionDidExit
-- First line of javascript --
[BeaconsDemo] start observing
[BeaconsDemo] requestAlwaysAuth
Any idea to handle this situation? Is there any way or approach to send the event through RCTEventEmitter waiting for the javascript is loaded?
Thanks
This general problem also exists when writing native beacon apps for iOS or Android. The solution is that you must set up your "hook" that enables beacon monitoring and adds the event listener (or delegate / notification callback as it is called in native code) in the appropriate place per platform:
iOS: AppDelegate.didFinishLanching(options: )
Android: Application.onCreate()
The key is that this setup must be complete before those methods return.
This same rule applies for ReactNative. The trick with ReactNative, is that by default JavaScript does not run on these events -- it runs only when a screen is launched, which sets up the hook too late for the above to work when your app is killed. To get this to work, you'll need to eject your app so you can set up some custom native code in the above callbacks.
Two options:
Implement beacon detection natively (easiest, but requires native coding on a per-platform basis)
Add native code in the above native callbacks that starts a RCTBridge on iOS, and launches a view that executes code that triggers our JavaScript code to set up beacon detection. On Android, the equivalent would be to construct a new ReactRootView and ReactNativeInstanceManager in a way that it triggers your JavaScript code to set up beacon detection.
I have not personally tested option 2 to see if it can set up the hooks soon enough. If it works, it will certainly be harder than the native solution, but allows you to keep your beacon detection logic in JavaScript.
(https://github.com/MacKentoch/react-native-beacons-manager/issues/50)
I've forked #newoceaninfosys' fork and added the 'missed beacon' method. Check my last 3 commits for how to replicate it.
(https://github.com/iamandiradu/react-native-beacons-manager)
Use it by adding this in your didMount function:
if (Platform.OS === 'ios') {
// iOS cold start listener
this.beaconMissedListener = Beacons.BeaconsEventEmitter.addListener(
'onMissedBeacon',
data => {
if (data) {
this._beaconListener(data);
}
},
);
Beacons.getMissedBeacon();
}
This will retrieve the data that got 'lost' because the event triggered faster than the listener.
(React Native (iOS): RCTEventEmitter before javascript is loaded)
Hope this helps someone. :)
I've been experimenting with responsive voice for my html5 application. Apart from responsive voice, my app works stand-alone, with no need for the Internet, because the schools where I work have unreliable Internet connections.
I previously had a problem waiting for RV's javascript to load on a slow connection, which I solved using the preloader yepnope:
yepnope({
load: 'https://code.responsivevoice.org/responsivevoice.js',
callback: function (url, result, key) {
if (typeof responsiveVoice!="undefined"){
//code to activate RV functionality here
}
}
});
While testing this out, I realised the potential for something much better: yepnope automatically times out after 10 seconds if the script doesn't load and triggers the callback function anyway. That timeout can be changed, but what I'd like is, effectively, no timeout at all!
For example, if the students start using my app at 07:30 a.m. when the school's satellite dish barely works, then at 3 p.m. the clouds clear to make RV viable, it would be nice if the script finally loaded, triggered the callback and RV sprang into life.
So I have 3 questions:
In principle, is there any reason why I should not change the yepnope timeout from 10 seconds, to 12 hours? e.g.
yepnope.errorTimeout = 43200000;
I notice that yepnope has been deprecated. Can anyone recommend a similarly easy-to-use preloader with a no timeout option?
Would it be lighter on system resources to use setInterval? e.g.
var net_check = window.setInterval(yepnope, 18000); // try loading the script every 5 minutes
then if RV loads, cancel the setInterval:
yepnope({
load: 'https://code.responsivevoice.org/responsivevoice.js',
callback: function (url, result, key) {
if (typeof responsiveVoice!="undefined"){
clearInterval(net_check);
//code to activate RV functionality here
}
}
});
Thanks as always for any advice.
Edit: #Steyn van Esveld raised a really good point: "Is there any reason you can't download the responsivevoice.js and load it locally?"
In fact the RV script doesn't provide the text-to-speech voices itself - it's more of a facilitator. If your browser has native t-t-s support, it will use it, if not, they generate audio files (presumably from their website) and send them to your browser. Also, even Chrome's native t-t-s support evaporates if you are off-line. e.g. if you run:
voices = window.speechSynthesis.getVoices();
voices.length
from the console when offline, it returns "0".
This means I need a fallback if the Internet goes down after my app loads. The most reliable way I've found to do this is:
var rvStarted=false;
responsiveVoice.speak(vocEx, {onstart:function(){rvStarted=true;}});
setTimeout(function(){
if (rvStarted==false){
responsiveVoice.cancel();
audVoc.play(); //plays a backup off-line recording
}
},1000);
There is an onerror callback in the RV api, but it's vaguely documented, and I certainly cannot control the timeout myself as I can with this script.
After more research and experimenting, I've got a solution that works. If anyone can come up with something more efficient, I'd be delighted. First the code (explanation follows):
function loadRV() {
console.log("Trying to load RV");
inject.js('https://code.responsivevoice.org/responsivevoice.js',
function() {
if (typeof responsiveVoice!="undefined"){
clearInterval(net_check);
console.log("RV loaded");
// code here to turn on RV functionality
}
else {
console.log("RV load failed");
}
});
}
var net_check=setInterval(function(){
loadRV();
},60000);
loadRV();
Now the explanation:
I realised that yepnope detected that there was no network connection during the first try, and thereafter made no attempt to load the javascript despite the network having been meanwhile turned on. The problem was not about timeout (thus the change in the question title.
So I searched around for a replacement for yepnope that would try to load the javascript each time it was called regardless of what had previously happened. I eventually came across inject.js. This is based on yepnope with some functionality removed and some added. I guess the ability to check if there is a network connection was removed, which suited my needs.
I then wrapped inject.js in a function and called it every minute with setInterval. I also call it once immediately so that RV functionality will be present if there is a viable Internet connection at initialisation.
Although this seems unwieldy, it's working very well so far. I've tested it with the three situations that I commonly face:
No network connection at all during startup; the computer connects to the network some time later.
A local network connection but no Internet connection during startup; the local network connects to the Internet some time later.
A very slow Internet connection during startup; the connection becomes viable some time later.
The setInterval has no appreciable effect on program execution, and the added functionality is quite transparent. If I connect the computer to the network and start a listening activity, about 1 minute later the RV voices will be added to my off-line recordings.
I doubt that many others face similar problems (I certainly would not wish them on anyone), but if so, I do hope this helps.
I have a cordova-based app that runs on Android, iOS and Windows Phone. The starting point in my app is the index.html page, which will not only be loaded on app start, but you can redirect to it from inside the app.
I want to execute some code if and only if the app was just started (so when the index.html was displayed the first time) and not if it was redirected to it. I tried to use cookies that expire when the session ends, but cordova does not work with cookies.
Also I do not want to use session storage because some older Androids (as well as Internet Explorer) cannot handle this.
My used cordova version is 4.0.0
EDIT:
I forgot to mention that it is not a single-page app, but I use multiple pages that can be accessed, so the deviceready-event does not work, because it would be fired every time I access the index.html-page
Try smth like this:
function documentReady() {
document.addEventListener("deviceready", handleDeviceReady, false);
}
function handleDeviceReady(event) {
//cordova api is ready for use
if (!localStorage.getItem('alreadyStarted')) {
// App just started!
localStorage.setItem('alreadyStarted', true);
}
}
UPD. Also you need to set the flag at first startup (i.e. alreadyStarted = true in sessions or LocalStorage) and check it in handleDeviceReady() later.
Write to the database at first run. Everytime the app starts, check the database to see if that string is present. If it it, ignore. Else, create and write.
I have a dashboard web-app that I want to play an alert sound if its having problems connecting. The site's ajax code will poll for data and throttle down its refresh rate if it can't connect. Once the server comes back up, the site will continue working.
In the mean time I would like a sound to play each time it can't connect (so I know to check the server). Here is that code. This code works.
var error_audio = new Audio("audio/"+settings.refresh.error_audio);
error_audio.load();
//this gets called when there is a connection error.
function onConnectionError() {
error_audio.play();
}
However the 2nd time through the function the audio doesn't play. Digging around in Chrome's debugger the 'played' attribute in the audio element gets set to true. Setting it to false has no results. Any ideas?
I encountered this just today, after more searching I found that you must set the source property on the audio element again to get it to restart. Don't worry, no network activity occurs, and the operation is heavily optimized.
var error_audio = new Audio("audio/"+settings.refresh.error_audio);
error_audio.load();
//this gets called when there is a connection error.
function onConnectionError() {
error_audio.src = "audio/"+settings.refresh.error_audio;
error_audio.play();
}
This behavior is expressed in chrome 21. FF doesn't seem to mind setting the src twice either!
Try setting error_audio.currentTime to 0 before playing it. Maybe it doesn't automatically go back to the beginning
You need to implement the Content-Range response headers, since Chrome requests the file in multiple parts via the Range HTTP header.
See here: HTML5 <audio> Safari live broadcast vs not
Once that has been implemented, both the play() function and setting the currentTime property should work.
Q: I’VE GOT AN AUDIOBUFFERSOURCENODE, THAT I JUST PLAYED BACK WITH NOTEON(), AND I WANT TO PLAY IT AGAIN, BUT NOTEON() DOESN’T DO ANYTHING! HELP!
A: Once a source node has finished playing back, it can’t play back more. To play back the underlying buffer again, you should create a new AudioBufferSourceNode and call noteOn().
Though re-creating the source node may feel inefficient, source nodes are heavily optimized for this pattern. Plus, if you keep a handle to the AudioBuffer, you don't need to make another request to the asset to play the same sound again. If you find yourself needing to repeat this pattern, encapsulate playback with a simple helper function like playSound(buffer).
Q: WHEN PLAYING BACK A SOUND, WHY DO YOU NEED TO MAKE A NEW SOURCE NODE EVERY TIME?
A: The idea of this architecture is to decouple audio asset from playback state. Taking a record player analogy, buffers are analogous to records and sources to play-heads. Because many applications involve multiple versions of the same buffer playing simultaneously, this pattern is essential.
source:
http://updates.html5rocks.com/2012/01/Web-Audio-FAQ
You need to pause the audio just before its end and change the current playing time to zero, then play it.
Javascript/Jquery to control HTML5 audio elements - check this link - explains How to handle/control the HTML5 audio elements?. It may help you!
Chrome/Safari have fixed this issue in newer versions of the browser and the above code now works as expected. I am not sure the precise version it was fixed in.
My project had audit module, which includes each and every action of the user to be recorded.
When the user closes the browser the audit regarding the logout has to be stored in the database.
I found one solution on the net, but it is working in my machine's IE but failed to work in the friends machine's IE why?
The code is:
window.onbeforeunload = clean_up;
function clean_up()
{
var flex = document.${application} || window.${application};
flex.myFlexFunction();
}
I placed this code in the index.template.html file in the html-template folder under flex src.
I also placed the below code in my main application.mxml file:
ExternalInterface.addCallback("myFlexFunction",btnLogout);
and I defined the logout function.
Ok, here is the deal. CAN NOT BE DONE RELIABLY. If this is for audit... you are out of luck and deliver a half baked approach to start with.
Why?
Go to your task manager, kill the IIS process - nothing logs out. No audit. Ergo - the solution does most likely not fulfill the legal audit requirements ;)
Another approach:
Call a service exvery X seconds from the running page. Like every 5 seconds.
Assume client dies when you dont receive call for 2 * X seconds (like after 10 seconds).
This way you realize when the client does not connect anymore. Does not stop the user from pulling the network cable and conrinuing viewing, so a failure of the audit method call should wipe the HTML content ;)
But at least you handle browser crashes / terminations, too.