React Native (iOS): RCTEventEmitter before javascript is loaded - javascript

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. :)

Related

How do I start streaming immediately after I started Jitsi API?

I have a Jitsi instance running in one of the pages of my Angular application. It works flawlessly.
Now I want a stream to start as soon as the API is initialized. I couldn't find any onLoad or onStart event of the API. So initially I tried this way:
this.api = new JitsiMeetExternalAPI("mydomain.com", this.options);
this.api.executeCommand('startRecording', {
mode: 'stream',
rtmpStreamKey: "MyKey",
rtmpBroadcastID: "MyId"
})
And it didn't work. After some fruitless search I've made it work moving execute command to a function and doing:
setTimeout(()=>this.startStream(), 10000);
But that's ugly and error-prone as hell. I imagine it should exist a better way .
How do I know that the API is fully initialized so I can start live stream ?
Edit
I tried onload event on API creation but it works only sometimes. Others it just ignores the command.

window.webContents.send() causes memory leak

I'm experiencing an issue in Electron where window.webContents.send() behaves as expected on the first execution, but after I call it again the function runs twice, then three times, etc.
Here's a stripped back example of one of the menubar functions I'm using to tell the renderer to package data for saving.
Main.js
{
label: 'Save',
click() {
window.webContents.send('myChannel', myArgs);
}
}
Renderer.js
ipcRenderer.once('myChannel', (evt, myArgs) => {
// package data for saving
});
My understanding is that Electron is creating a new event handler each time I click save in the menubar. I can't seem to find any documentation on how to dispose the event handler in the electron docs.
Here's a link to a similar problem. The solution isn't of much help to me, as I need these functions to only be run when they're called from the menubar, not when the app first launches.
Thanks!

Event listener works in on context but not another - code identical, and no errors observed. Possible causes?

I'm bamboozled. I use a select2 widget on a web page, implemented by django-autocomplete-light, and I attach an event listener to the it as follows:
const game_selector = $("#"+id_prefix+"game");
game_selector.on("change", switchGame);
Works a charm. I select a new game in the select box and the switchGame function is called.
That is running on Django development server with manage.py runserver. And I can see how groovy this is in Chrome's debugger:
There it is, switchGame(event) is the handler. And it all works too. No drama.
But I publish the code to my webserver and suddenly it doesn't work. The event listener never fires. switchGame is never called.
It's serving the self same code, looks the same in the client and in Chrome's debugger. All fine. I can even see the event listener is attached, albeit the order is different:
Served by the development server there there is a select2 handler above switchGame in the list and served from the production server the same select2 handler is listed below the switchGame handler.
Can the order matter? And why would the order differ?
In the end I am looking at the same binding code above, and I can set a breakpoint inside switchGame and cI can confirm served by the django runserver switchGame runs when I select a new game, but served from the production server it does not.
Suspect some jquery issue I even changed the listener binding to:
const game_selector = $("#"+id_prefix+"game");
//game_selector.on("change", switchGame);
game_selector.get(0).addEventListener("change", switchGame);
But the behaviour is the same. Bamboozles me how Javascript might function differently based on the server context? leads me to suspect different Javascript being served, but every check I have made speaks against that, as in I can make an edit, publish and reload web page from productions server and the changed javascript is there and visible in Chrome's debugger.
I wonder if anyone has ever experience such bizarre behaviour before and is in a position to comment or to suggest further diagnostic directions.
This is a nasty side effect of loading the jQuery library twice.
I was loading it explicitly and a widget I was using loaded it again implicitly. A bug!
Discussed in depth here:
github.com/yourlabs/django-autocomplete-light/issues/1079
and not yet fixed, but workaround is that I don't load jQuery explicitly any more. And the issue disappears.
Sadly this does not explain the symptoms very well but it does provide a solution to what was being experienced. The same code was running in two contexts working in one and not the other. Remove the jQuery reload and it works in both.

How can I run background tasks in React Native?

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

Cordova with Angular, how to get deviceready event

I have been having various issues with the device ready event, from the deviceready has not fired after 5 seconds error to not having the plugins work.
From what I understand from my research, the following is required (please correct me if i'm wrong):
angular must be loaded before the deviceready event or it wont pick it up
cordova.js must be loaded in less than 5 seconds
So my question is - how do I get cordova.js loaded as quick as possible while making it wait for angular to be ready for the deviceready event?
I use manual bootstrapping to get it work (perfectly)
function bootstrapAngular() {
var domElement = document.querySelector('html');
angular.bootstrap(domElement, ['appName']);
}
if (document.URL.indexOf('http://') === -1 && document.URL.indexOf('https://') === -1) {
// URL: Running in Cordova/PhoneGap
document.addEventListener("deviceready", bootstrapAngular, false);
the if because my app is accessible form browser (either "http://" or "https://") and cordova("file://").
Load cordova.js after loading angular, and error 'deviceready not fired after 5 seconds' you will face only on desktop browser, instead when you will try on actual device or emulator, the event will fire.
You can use Corvoda Mocks to simulate the device's state on Chrome for testing your app.
You need to load AngularJs before Cordova.
If you want, here's a working seed:
https://github.com/marioaleogolsat/cordova-angular-angularMaterial-seed
Just remember that some plugins can't be used in a emulation, so use cordova run --device to make sure that anything is working properly.

Categories

Resources