how to work with react-native's PushNotificationIOS.getInitialNotification - javascript

I'm trying to detect whether my react-native app was launched by the user tapping a push-notification banner (see this excellent SO answer on the topic).
I've implemented the pattern Mark describes, and have discovered that the "notification" objects being provided by PushNotificationIOS.getInitialNotification are really bizarre, at least in cases when there isn't a notification to retrieve. Detecting this case has been a PITA, and I'm actually quite confused.
From what I can tell, PushNotificationIOS.getInitialNotification returns a promise; this promise is supposed to resolve with either null or an actual notification object -- null when there is no notification waiting for the user. This is the scenario I'm trying to detect and support.
Here's why it's such a pain to detect; the following tests were all run when there is no notification to find:
// tell me about the object
JSON.stringify(notification);
//=> {}
// what keys does it have?
Object.keys(notification);
//=> [ '_data', '_badgeCount', '_sound', '_alert' ]
So it stringifies to empty, but it has four keys? K...
// tell me about the data, then
JSON.stringify(notification._data);
//=> undefined
// wtf?
These bizarre facts frustrate both my understanding and my ability to distinguish between cases where there's an actual notification to react to vs. cases where the mailbox is empty. Based on these facts, I assumed I could test for the members I want, but even the most careful probing produces false positives 100% of the time:
PushNotificationIOS.getInitialNotification()
.then((notification) => {
// usually there is no notification; don't act in those scenarios
if(!notification || notification === null || !notification.hasOwnProperty('_data')) {
return;
}
// is a real notification; grab the data and act.
let payload = notification._data.appName; // TODO: use correct accessor method, probably note.data() -- which doesn't exist
Store.dispatch(Actions.receivePushNotification(payload, true /* true = app was awaked by note */))
});
Every time I run this code, it fails to trigger the escape hatch and throws on let payload because undefined is not an object (evaluating 'notification._data.appName').
Can someone explain what's going on here? Is PushNotificationIOS.getInitialNotification broken or deprecated? How in JS is it possible to have a key that evaluates to undefined? How can I detect this scenario?
Experienced javascripter, pretty puzzled here. Thanks for any help.
BTW: using react-native v0.29.0

The notification is an instance of PushNotification, not a plain object, that's why it stringifies to an empty object since no custom toString was implemented for it.
It sounds like a bug to me (which should be reported if not already) that the object is created when no notification is available.
Anyway, to workaround this issue, your check should actually be:
if(!notification || !notification.getData()) {
return;
}
Update: Issue has been fixed in 0.31 - see Github issue for more details.

Related

Overriding Object.prototype.get to Prevent Null Check Issues in Large App

I realize this is not recommended by the JS community but I think have a good use case for overriding the Object.prototype so that accessing properties of undefined objects returns undefined instead of throwing an app crashing error.
Little background information:
Working with a large app (about 200k lines of code)
App is extremely configurable with loads of options - there's no way of testing every possibility
Angular 2/Ionic 2 project
Basically, I'd like to make it so that the following code console logs 'undefined' instead of generating an error:
Pseudo code:
Object.defineProperty(Object.prototype, Object.prototype.get, (val) => {
try {
console.log(val);
} catch (e) {
console.log('Attempted to access undefined property');
}
});
const obj = {
a: 'b'
};
console.log(obj.a.b.c); // should log undefined but actually generates an app freezing issue
Questions:
How can I make this work? Is there another way I can prevent null check errors across the entire app without adding too much code? Is there a generic fix for making it so that these null check errors do not crash the Angular/Ionic app?

Mixpanel returning a response object instead of undefined even when opted out in AVA Unit Test

I'm trying to implement an AVA Unit Test for my mixpanel implementation. To do this, I'm comparing the result of mixpanel.track() where if it returns anything, the track was successful, otherwise, it should be undefined.
I thought maybe it was that it was using a different mixpanel instance so I tried creating a named instance and ensuring that but it was to no avail. I'm also trying the same process but with Amplitude and it seems to be working fine (when I am opted out, the response fails as expected)
I have done this in my components where if
const test = mixpanel.track('event_name', {}) is successful, !!test === true but if I do mixpanel.opt_out.tracking() prior to const test = mixpanel.track('event_name', {}), then !!test === undefined.
Expected behaviour (and the observed behaviour when I use it in my components):
trackResponse === undefined
Observed behaviour:
trackResponse === { event: 'asdf',
properties:
{ '$browser': 'Safari',
'$current_url': 'about:blank',
'$browser_version': null,
'$screen_height': 0,
'$screen_width': 0,
mp_lib: 'web',
'$lib_version': '2.30.1',
time: 1572898982.142,
distinct_id: '[some_id]',
'$device_id': '[some_id]',
'$initial_referrer': '$direct',
'$initial_referring_domain': '$direct',
token: '[token]' } }
where [some_id] and [token] are some distinct values I've deleted.
I don't understand why in the AVA test, I'm receiving a response when normally a failed track() results in an undefined response. Could someone shine some light on this?
Let me know if I need to provide any additional information. Thanks.
I figured it out in case anyone else runs into this issue.
I used a debugger to step into the mixpanel.track() calls and figured out that to see if the user had opted out, mixpanel checks for a property in the localStorage and compares it to see if it's === to '0'. If this fails, it assumes the user has not opted out and carries out the track call as normal.
I guess during the AVA test, it was unable to access this property and assumed the user had not opted out. To fix it, in my call to mixpanel.init(), I added opt_out_tracking_persistence_type: 'cookie' as an option so that my opt_out call was being saved somewhere that the property could be accessed during the test.

How .__defineGetter__ can be used to detect if the console in a browser is open

I have been reading through the source code here which detects when the console is open and if so plays an audio element.
I am confused by a couple of things.
Firstly, I cannot figure out how it detects that the console is open.
if( audioCheck === true || (window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized)) {
play();
}
The lines below return undefined so it is audioCheck === true that triggers the play() function.
window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized
It is the following line that sets audioCheck to true. I have done some research on __defineGetter__ but it remains unclear for me.
audioElement.__defineGetter__('id', function() {
audioCheck = true;
});
Can anyone explain what this does? I cannot figure it out after much research.
And finally what completely throws me is that if I remove this line the audio doesn't play:
console.log("Check the console: ", audioElement);
This is really strange as I would have though this should only log the info to the console, and not have any effect on whether or not the sound is played, but I guess this must tie back in with the __defineGetter__
There are a number of questions in here so any light that could be shed on these would be greatly appreciated.
I don't know of any sources to confirm this1, but most likely the behavior you are experiencing is an effect of what modern browsers' consoles usually do — if you issue console.log() and pass an HTML element as its argument, the console will attempt to display its details such as the ID. And the __defineGetter__ line ensures that when the browser accesses the ID, the fake getter function gets invoked (__defineGetter__() is just a non-standard way of setting — or replacing — a getter property on an object2). Quite smart, isn't it?
1Probably analysis of the source code of various browsers would be required to fully understand and validate the issue.
2Read more on getters at MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get (note that the standard way, as described therein, is to call Object.defineProperty()).

When developing a library, should we throw errors/exceptions?

I'm developing a pub/sub Mediator library to be used by others and I don't know how to handle errors.
Here's an example piece of code:
/**
*
* #param String channel Channel to subscribe
* #param String|function callback Function name or callback
* #param String context Context to bind function to
* #param Boolean once True to subscribe once
**/
this.subscribe = function (channel, callback, context, once) {
if (!_.isObject(context)) {
context = window;
}
if (!_.isFunction(subscription) && !_.isFunction(context[subscription])) {
throw new Error('Function passed as callback to channel subscription named ' + channel + ' seems invalid!');
}
// Everything ok, add to channels object
channels[channel].push({fn: subscription, context: context || this, once: once});
}
This method is part of the library. It's API expects a channel name and a valid callback (either the function name, a lambda or a callback).
If the channel argument is not a string, the library readily stop. If an invalid context is passed, window is assumed. Mistakes in both these parameters are easily debugaable.
However, if an invalid callback is passed, it propagates the error until an event is fired (a channel publishing is made). In a pub/sub system this might become a nightmare to debug if, for instance, the event is rarely fired.
So my question is...
In this specific scenario, taking in account that I'm developing a javascript library to others, should I:
throw Errors to prevent further error propagation?
return false/undefined/-1 and not register the subscription?
proceed as normal at let it die somewhere, leaving the debugging to the third party developer
NOTE:
There's a similar question but my situation is a bit different and the answers provided didn't put my spirit at ease.
I guess this answer is more of my opinion and what I follow. You have to ask yourself if you should return throw an error and let the consumer, of your library, handle the error. If the error is something that cannot be recovered from, there is no need for you to throw an error (in JS). Your library should be able to fallback gracefully to alternatives but if it cannot do that for a certain function, it should return undefined. Most JS developers do check for undefined and it's a pretty common practice to do so while using library functions. Returning false is usually done in event handlers if an event cannot be handled.
Option #3 should be avoided. At least, return false;. You even might register the invalid callback, expecting that no events are published as well (since something went wrong). This "silent fail if not everything went wrong" might not really be applicable to your pub/sub example, yet there might be use cases for it.
Option #2 sounds good. It somehow makes the callback parameter explicitly optional, which could be a feature of your library - if the user is not sure himself whether he has a reason to subscribe, he can omit that test if you are doing it anyway. You might combine this with a console.warn() message in debugging versions.
Option #1 should be chosen if something really unexpected happened. It allows you to fail with a very descriptive custom Error message. This could be the case if the callback is a truthy object, but not callable, or the channel argument is a boolean or something - depending on how closed your interface design is / how much overloading you provide (in your case you could accept strings to be evaled as callbacks, or arrays of channel strings for example).
Errors and Exceptions are supposed exactly to stop the execution and notify the caller about it.
"throw Errors to prevent further error propagation?" is exactly write choice if your code is not supposed to be executed in that case.
Still, if you expect something to be passed wrong, you can return some special value and describe it in the documentation. Default values are widely used by developers, that's not intuitive,but mostly works tho:
/*
* ...
* Returns a Socket if found, otherwise undefined
*/
function FindSocketOrUndefined(): Socket|undefined {
...
return undefined
}

Bad idea to leave "console.log()" calls in your production JavaScript code?

I have a bunch of console.log() calls in my JavaScript.
Should I comment them out before I deploy to production?
I'd like to just leave them there, so I don't have to go to the trouble of re-adding the comments later on if I need to do any more debugging. Is this a bad idea?
It will cause Javascript errors, terminating the execution of the block of Javascript containing the error.
You could, however, define a dummy function that's a no-op when Firebug is not active:
if(typeof console === "undefined") {
console = { log: function() { } };
}
If you use any methods other than log, you would need to stub out those as well.
As others have already pointed it, leaving it in will cause errors in some browsers, but those errors can be worked around by putting in some stubs.
However, I would not only comment them out, but outright remove those lines. It just seems sloppy to do otherwise. Perhaps I'm being pedantic, but I don't think that "production" code should include "debug" code at all, even in commented form. If you leave comments in at all, those comments should describe what the code is doing, or the reasoning behind it--not blocks of disabled code. (Although, most comments should be removed automatically by your minification process. You are minimizing, right?)
Also, in several years of working with JavaScript, I can't recall ever coming back to a function and saying "Gee, I wish I'd left those console.logs in place here!" In general, when I am "done" with working on a function, and later have to come back to it, I'm coming back to fix some other problem. Whatever that new problem is, if the console.logs from a previous round of work could have been helpful, then I'd have spotted the problem the first time. In other words, if I come back to something, I'm not likely to need exactly the same debug information as I needed on previous occasions.
Just my two cents... Good luck!
Update after 13 years
I've changed my mind, and now agree with the comments that have accumulated on this answer over the years.
Some log messages provide long-term value to an application, even a client-side JavaScript application, and should be left in.
Other log messages are low-value noise and should be removed, or else they will drown out the high-value messages.
If you have a deployment script, you can use it to strip out the calls to console.log (and minify the file).
While you're at it, you can throw your JS through JSLint and log the violations for inspection (or prevent the deployment).
This is a great example of why you want to automate your deployment. If your process allows you to publish a js file with console.logs in it, at some point you will do it.
To my knowledge there is no shorter method of stubbing out console.log than the following 45 characters:
window.console||(console={log:function(){}});
That's the first of 3 different versions depending on which console methods you want to stub out all of them are tiny and all have been tested in IE6+ and modern browsers.
The other two versions cover varying other console methods. One covers the four basics and the other covers all known console methods for firebug and webkit. Again, in the tiniest file sizes possible.
That project is on github: https://github.com/andyet/ConsoleDummy.js
If you can think of any way to minimize the code further, contributions are welcomed.
-- EDIT -- May 16, 2012
I've since improved on this code. It's still tiny but adds the ability to turn the console output on and off: https://github.com/HenrikJoreteg/andlog
It was featured on The Changelog Show
You should at least create a dummy console.log if the object doesn't exist so your code won't throw errors on users' machines without firebug installed.
Another possibility would be to trigger logging only in 'debug mode', ie if a certain flag is set:
if(_debug) console.log('foo');
_debug && console.log('foo');
Hope it helps someone--I wrote a wrapper for it a while back, its slightly more flexible than the accepted solution.
Obviously, if you use other methods such as console.info etc, you can replicate the effect. when done with your staging environment, simply change the default C.debug to false for production and you won't have to change any other code / take lines out etc. Very easy to come back to and debug later on.
var C = {
// console wrapper
debug: true, // global debug on|off
quietDismiss: false, // may want to just drop, or alert instead
log: function() {
if (!C.debug) return false;
if (typeof console == 'object' && typeof console.log != "undefined") {
console.log.apply(this, arguments);
}
else {
if (!C.quietDismiss) {
var result = "";
for (var i = 0, l = arguments.length; i < l; i++)
result += arguments[i] + " ("+typeof arguments[i]+") ";
alert(result);
}
}
}
}; // end console wrapper.
// example data and object
var foo = "foo", bar = document.getElementById("divImage");
C.log(foo, bar);
// to surpress alerts on IE w/o a console:
C.quietDismiss = true;
C.log("this won't show if no console");
// to disable console completely everywhere:
C.debug = false;
C.log("this won't show ever");
this seems to work for me...
if (!window.console) {
window.console = {
log: function () {},
group: function () {},
error: function () {},
warn: function () {},
groupEnd: function () {}
};
}
Figured I would share a different perspective. Leaving this type of output visible to the outside world in a PCI application makes you non-compliant.
I agree that the console stub is a good approach. I've tried various console plugins, code snippets, including some fairly complex ones. They all had some problem in at least one browser, so I ended up going with something simple like below, which is an amalgamation of other snippets I've seen and some suggestions from the YUI team. It appears to function in IE8+, Firefox, Chrome and Safari (for Windows).
// To disable logging when posting a production release, just change this to false.
var debugMode = false;
// Support logging to console in all browsers even if the console is disabled.
var log = function (msg) {
debugMode && window.console && console.log ? console.log(msg) : null;
};
Note: It supports disabling logging to the console via a flag. Perhaps you could automate this via build scripts too. Alternatively, you could expose UI or some other mechanism to flip this flag at run time. You can get much more sophisticated of course, with logging levels, ajax submission of logs based on log threshold (e.g. all Error level statements are transmitted to the server for storage there etc.).
Many of these threads/questions around logging seem to think of log statements as debug code and not code instrumentation. Hence the desire to remove the log statements. Instrumentation is extremely useful when an application is in the wild and it's no longer as easy to attach a debugger or information is fed to you from a user or via support. You should never log anything sensitive, regardless of where it's been logged to so privacy/security should not be compromised. Once you think of the logging as instrumentation it now becomes production code and should be written to the same standard.
With applications using ever more complex javascript I think instrumentation is critical.
As other have mentions it will thrown an error in most browsers. In Firefox 4 it won't throw an error, the message is logged in the web developer console (new in Firefox 4).
One workaround to such mistakes that I really liked was de&&bug:
var de = true;
var bug = function() { console.log.apply(this, arguments); }
// within code
de&&bug(someObject);
A nice one-liner:
(!console) ? console.log=function(){} : console.log('Logging is supported.');
Yes, it's bad idea to let them running always in production.
What you can do is you can use console.debug which will console ONLY when the debugger is opened.
https://developer.mozilla.org/en-US/docs/Web/API/console/debug

Categories

Resources