Application.Properties does not work for a minority of users

One of my apps gets ERA reports for "Unhandled Exception" and "Invalid Value" in lines of code related to properties.

In this specific case:

1) I get "Unhandled Exception" in cases like:

var a = Application.Properties.getValue("property1");

The documentation says that getValue() can throw UnexpectedTypeException and InvalidKeyException, which both are not the case because I always use Strings as keys and the key does always exist because defined in the properties.xml file.

2) I get "Invalid Value" in cases like:

var a = Application.Properties.getValue("property1");
if(a == 1) {
    // do stuff
}

Differently from before, it looks like the getValue() function retrieves something, because no exception is thrown, but the type is different from what supposed to be.

This is really strange because these issues happen to a very small portion of the users, and in fact I haven't been able to replicate this problem on both of my devices with daily usage of my app.

Thank you very much!

  • See https://forums.garmin.com/developer/connect-iq/w/wiki/4/new-developer-faq#settings-crash

    you'll need to adapt it for Application.Properties.setValue/getValue but I do this in every apps that uses App Setting

  • I second this new phenomenon. I have not seen these errors for almost a year and 50k users, and in the last week started to get them, so it looks like there are some problems that is outside of my code. The errors I get are different but also related to getValue and make no sense:

    Error Name: Unexpected Type Error
    Occurrences: 12
    First Occurrence: 2022-12-28
    Last Occurrence: 2023-01-04
    Devices:
    vívoactive® 4: 7.80
    App Versions: 2.1.0
    Languages: fre
    Backtrace:
      Config.getConfigNumber:83 var value = Properties.getValue("i");

    Error Name: Unhandled Exception
    Occurrences: 1
    First Occurrence: 2022-12-29
    Last Occurrence: 2022-12-29
    Devices:
    Edge® 830: 9.73
    App Versions: 2.1.0
    Languages: eng
    Backtrace:
      Config.getConfigStr:58  var value = Properties.getValue("D");

    The only 2 explanations I could come up with are:

    1. since both reports have only 1 language, 1 device maybe both happen only on 1 faulty device.

    2. maybe the error name is misleading and it was caused by the memory being full. I had seen in the past strange errors to be thrown in the simulator in scenarios when Out of memory error would have been a better exception.

  • This has been a issue since 2016, comes and goes, and varies by Android or  iOS.  That's why I use the code (well it's changed a bit over the years!) that's in the link to the New Developer FAQ in EVERY app.  With 1.7m downloaded apps, I haven't seen this issue causing a crash..

  • I use that code (modified to my purpose), but how could that possibly help when the exception in both cases was thrown on the exact same line:

    import Toybox.Application.Properties;

    function c(k as PropertyKeyType) {
       var value = Properties.getValue(k);
       // the rest of the code is irrelevant as the exception is thrown on the above line
    }
    function d() {
        c("D");
    }

    and property "D" exists (at least in the prg/iq uploaded to the store :) 

  • Also trying to catch the exception and provide a default value might avoid crashes, but defeats the purpose of having user-defined properties.

  • If a key in Properties don't exist. that will throw an exception.

    Here's my current version of that function.  It requires you set the boolen "HasNewStorage" for your class.  Notice the try/catch.

    And it works on devices that only have getProperty()

    	function readKeyInt(myApp,key,thisDefault) {	
    	    var value=null;
    	    if(hasNewStorage) {
    	    	try {
    	    		value = App.Properties.getValue(key);
    	    	} catch(e) {
    	    		value=null;
    	    	}
    	    } else {value = myApp.getProperty(key);} 	
    	    if(value==null || !(value instanceof Number)) {
    			if(value!=null) {value=value.toNumber();} 
    			else {value=thisDefault;}
        	}    	
    		return value;    
    	}

  • errors is in the line App.Properties.getValue(key) or myApp.getProperty(key)

    I don't use line
    var value=null;
    my func has parameters getPRP(val, key...) and I use val instead of additional var

    Sometimes val is null sometimes int etc, let's say val is int and property for key is string

    val = App.Properties.getValue(key);

    Can it make error? Does it mean system first check the type of val than try to convert  data from settings to int instead:

    - read data from properties

    - assign it to val

  • This can be simplified for clarity and to save code (which saves precious memory for data fields, for example).

    function readKeyInt(myApp, key, thisDefault) {	
        var value = null;
        if (hasNewStorage) {
        	try {
        		value = App.Properties.getValue(key);
        	} catch (e) { }
        } else {
            value = myApp.getProperty(key);
        } 	
        if (value == null) {
            value = thisDefault;
        } else if (!(value instanceof Number)) {
    		value = value.toNumber();
    	}    	
    	return value;    
    }

    An even leaner version which sacrifices a tiny bit of efficiency:

    function readKeyInt(myApp, key, thisDefault) {	
        var value = null;
        if (hasNewStorage) {
        	try {
        		value = App.Properties.getValue(key);
        	} catch (e) { }
        } else {
            value = myApp.getProperty(key);
        } 	
        if (value == null) {
            value = thisDefault;
        }	
    	return value.toNumber();    
    }

    I hate the fact that we have to handle the null case here (even with classic getProperty()), since it means defaults have to be duplicated in the properties XML and the app code (another case of wasteful design.) One example from the past is if the user entered invalid data in settings (such as non-numeric characters for a number, or a number that's out of range), the Garmin Connect app would send all nulls back to the device, instead of sending all defaults or maybe just the default for invalid field. (Haven't tried this in years, so I'm not sure how it works today.)

  • No, typecheck is compile time only thing

  • 1. Why not moving the getProperty as well inside the try?

    2. I hate to see errors in ERA, but i also try to think about the user experience: when there's some problem (settings file is corrupted somehow) and i enter a number in the settings, but see some other (the default) it's strange. I get angry about this buggy app... Isn't it better not to catch these type of errors? Sometimes it's better to know there's a problem, maybe delete (and hopefully reinstall) the app.

    I don't know which is preferable, and it might as well depend on which property it is. If it only changes the display color then using the default makes sense but if there's no logical default (it's a"required" field we can't guess, and the only reason we have a default is technical) i.e the user's age that is required for some calculation.

    Of course the best thing would be not useful for devices with low memory, but maybe for newer devices i would catch the error and return it and display it or rather a custom error that tells the user to reinstall the app and if that doesn't work connect the developer.