ERA reports some cases of unhandled exception from Application.Properties.getValue() in onLayout()

I'm getting some ERA reports of an unhandled exception when calling Application.Properties.getValue() from onLayout() - but I've no idea why it could be happening ...

It is happening in both an app and a watchface - and it's always on the very first call to access the properties after starting up.

The property is defined in the properties.xml just like normal:

    <property id="pg" type="boolean">false</property>  <!-- position got -->

And is accessed just with a normal call:

    var v = Application.Properties.getValue("pg");

It clearly works for most people using the app, but why are there some instances of exceptions being created?

The documentation says these are the possible exceptions:

  • (Toybox::Lang::UnexpectedTypeException)

    Thrown if key is of type null, Array, or Dictionary

    (Toybox::Application::Properties::InvalidKeyException)

    Thrown if key does not exist in Application Settings

Is there some other exception possible here?

Any suggestions for how I could debug this?

  • Although the documentation seems to imply Application.Properties and Application.AppBase.set/getProperty both use the .SET file, so maybe they are really the same

    Accessing Properties and Settings: Application.Properties

    Connect IQ 2.4 introduces the Application.Properties module, which provides an interface for accessing the values and properties of settings. Information is automatically saved on disk to a .SET file when Application.onStop() is called. To get or set a property value use the Application.Properties.getValue() or Application.Properties.setValue() methods, respectively:

    // Set an Object Store app setting
    Application.Properties.setValue("mySetting", mySetting);
    
    // Get an Object Store app setting value
    var mySetting = Application.Properties.getValue("mySetting");

    Accessing Properties and Settings: Object Store

    Prior to Connect IQ 2.4, properties and settings were accessed the same way Object Store storage values are accessed. This functionality is still available, though use of the Application.Properties module is preferred on devices that support Connect IQ 2.4 or higher.

    Object Store properties and settings are saved on disk to a .SET file when Application.onStop() is called. To get or set a property value use the Application.AppBase.getProperty() or Application.AppBase.setProperty() functions respectively:

    // Set an Object Store app setting
    Application.getApp().setProperty("mySetting", mySetting);
    
    // Get an Object Store app setting value
    var mySetting = Application.getApp().getProperty("mySetting");
  • On the sim or on a device?  150ms seems way high.  With storage you might want to use an approach where you dont store 20 keys, but use one key for 20 values. (an array)

    And remember, this isn't an either/or.  You can use Properties as well as Storage.

    If you run into speed issues, you can always use the old object store for local storage.

    Consider this.  You have 10 boolean settings, and your app generates 2k of data you want to save.  It really doesn't make sens for that 2k to be a property, as it's involved in a things wehn it comes to app settings.

  • Yes, they both use the .set file.  You can see this by looking in the apps\settings folder in the sim or in a device.

    The old ob jest store uses .str file in apps\data, while Storage uses a different scheme with a few files in apps\data.

  • consider this case and Storage using your times:

    var arr=[0,1,2,3,4,5,6,7,8,9,0];

    for(var i=0;i<arr.size();i++) }

      Application.Storage.setValue("key"+i,i);

    }

    at .1 sec for each, that's a second

    but if you do

    Application.Storage.setValue("key",arr);

    that's .1 seconds, and you have the same data stored.  You'll see the same with getValue()

  • Yep, that timing was on the simulator, so maybe not realistic. But reading some other threads about storage vs properties the speed still concerned me.

    I think I will have to go back to using Storage though as it is looking like the .SET file has somehow got corrupted like you suggested. But I have no idea how it can happen. Luckily I've now had a user contact me who has the crash, so hopefully I can get the bad .SET file from him and experiment with it myself.

    So I can use storage for everything that needs saving, and just use 1 load like you say. And then make all the reading of properties use try/catch, and assume they could fail at any time.

    And hopefully the storage won't fail Slight smile

    It seems odd though that there would be so many occurrences of .SET files going wrong for so many people, and on different devices. As the number of crashes on ERA from it is still going up.

  • Ok I've just tried switching what data I can out of being properties and moved it back into storage. In onLayout() I do a single read from storage and then in onStop() I do a single write to storage ... and you are right Slight smile I can't actually notice any difference in speed when switching to and from the watchface, when compared to using properties. I must have been doing multiple reads from storage when I tried it before.

    In fact the code works out a lot nicer as well, so thanks for suggesting it! I don't remember being able to store an array of arrays in storage, but it's a very cool feature - it makes it easy to build a single array to bring together data from different sources just for the purpose of doing a single write.

    So I think using a try/catch when reading any of the remaining property values will work now. Just set the default value if an exception is thrown like you suggested. And also display a temporary message to the user saying what has happened.

  • I've received the .set file from the user who had the problem - and just opening it with notepad on my PC I can see the property ids and data - but only for properties with an associated setting. I had some properties with no associated setting, and I can't see any of those in the .set file - so that would explain why the exception was occurring. Those are the ones which I'm now moving to storage, so that should stop it happening in future.

    I'm not sure what would cause them to get removed from the .set file though. 

    If I look at the .set from my own watch in notepad then I can see they are all there in the file.

  • I experience the same issue in my apps. I get an unhandled exception on lines which read property without setting.
    I checked the .SET file too and that property was missing. The strange thing is only 1 of my 3 apps give this error on my device (so it may not be issue with Firmware update and I did not uploaded new version to these apps) and only reinstallation could solve it... and it works fine now again, but I can't expect users to always reinstall...

    For me Storage is not an option as these properties are device specific, eg. overwriting this property on devices with low memory. And a default value with try catch wouldn't work also because after this error occurs on devices with larger memory would still display only the limited things which a low memory device is capable eg. get a random motivational quote from 40 or 140 is different.

    Can anyone recommend a way how this could be solved? I may add a readonly property in settings which would store these values, but that may just confuse the users about what that field is...

    (By the way I experience some strange things with garmin app settings lately, Anyone experience similar issues or is it just me?
    - on my phone settings are not available in the Connect and redirects me to connect iq store's settings... but if I uninstall the iq store the Connect's settings are working well again
    - Some of my readonly settings became enabled in the meantime (at release a user reported the a setting is not availabe, and that was because he has a device where that option shouldn't be available, so that was ok. Now he reported it is available but crashes the app... and I did not uploaded new version to this app either)

  • I'm experiencing the same error in ERA. I guess the best alternative is to use the following:

    Application.getApp().getProperty("mySetting");

    But the docs say it's deprecated, so not sure when they'll deprecate it. Doesn't feel good to use it.
    Those properties should just work, even when they're not set via the settings. Think this is still a bug that Garmin should solve

  • put in a try/catch.

    function getdata(key,default) {
        var ret=default;
        try {
            ret=Application.Settings.getValue(key);
        } catch(e) {
            System.println("err="+e);
        }
        return ret;
    }
            

    If something is wrong with the key in the .set, your app won't crash and you can create a log to see what the exception is.

    Do you have settings for your property?  Having a property without settings may cause an issue in some cases.