How to design a data field that records fit fields and plays well with other fields

Problem: developer creates a data field that works perfectly alone or with even some other CIQ data fields, but can crash for reasons caused by other developers' data fields, because of the limitations of number of fit fields per field type per activity or on the total number of bytes all the fields (per type) can have.

Unfortunately neither of the 2 errors I see in ERA (that according to my understanding are caused by either having more than 16 fit fields of the same type => System Error, or by having too many bytes of the fit fields' size alltogether => Out Of Memory Error) can be caught with try/catch :( 

I know this came up a few times in the past, and I know most developers think that Garmin should do something with this, but until that, what do you recommend to do? I could think of not very useful things like:

- ignore the System error, Out of memory error in ERA when it's from the function that creates the fit fields => crashes => bad user experience

- add some explanation about how other fields added to the same activity can cause my data field to crash => not the best user experience (most won't even read)

And then one that can be a bit more useful:

- add settings for each of my fit fields to be able to enable/disable them separately, and by that hopefully users with crash (and who read the description...) will be able to keep the more important fit fields while giving up on the less important => adds lot of code

  • I try to reduce fit fields in my data fields to the ones that are really of interest. It is usually enough to prevent crashes for watches because 2 data field limit makes difficult to reach 16 fit fields.

    However, for edge (10 data fields) it is not enough.

    So, I am implementing same option for enabling/disabling every single fit fields in settings.

    Unfortunately, the limit for 16 fields is not known by users. They usually don't read any instruction in the apps description either.

    So I inform them when I receive any email complaining about this crash. It is quite easy to detect because almost in all the cases, the complain looks similar:

    - your app has suddenly stopped working. I have not updated it or modify any configuration

    - after installing your data field, another data field X, that was working fine till now, is crashing

    Some kind of alert by Garmin when you assign a new data field is required. Similar to the current count of available CIQ data fields (x/2) for watches, (X/10) for edge. 

  • Some kind of alert by Garmin when you assign a new data field is required. Similar to the current count of available CIQ data fields (x/2) for watches, (X/10) for edge. 

    I think that would never work because Garmin has no way to concisely explain the problem and the user has no way to decide what to do.

    Garmin: "Uhhh one of your CIQ data fields might crash if you keep both of them"

    User: "Why didn't you prevent this from happening in the first place? What am I supposed to do now?"

    Garmin can't even reliably detect the problem in all cases, because the number of times createField() is called can't be known statically (it can change based on settings, for example.) I guess Garmin could be really clever and start an isolated "trial run" of each data field app. In each trial run, they could count number of calls to createField() before the point that createField() is no longer allowed, then terminate the app. After all 1 or 2 trial runs are complete, they could decide whether or not to show the alert. In this case they could show detailed information about how many fields each app uses and what the problem is, but it still may not be helpful for the user. Now they either have to find some settings in the apps to reduce the number of fields (if possible) or they have to uninstall 1 (or both) fields. After all that work Garmin would have done in this case, it still looks bad on them.

    I also think that displaying any kind of message/warning will only serve to annoy the user and suggest that this problem is more Garmin's fault than the developer's fault. While I agree it is Garmin's fault, Garmin would probably prefer to leave the user with the impression that the dev did something wrong.

    The design here is inherently flawed (what a shocker), and I think the real fix would be to change the design so each app has its own limit, but maybe that isn't possible or desirable for whatever reasons.

  • Here's the support article for Stryd Zones, a data field that records lots of fields based on data from a running power footpod:

    Connect IQ! Error message in the Garmin Power Field

    If you get the IQ! symbol on your Garmin watch instead of your normal power reading, something is wrong with your Garmin watch. These are the recommended steps to troubleshoot your Garmin:

    1. Restart the Garmin (depending on the watch, you may need to power it off / on).

    2. Follow the Garmin instruction manual to restore factory defaults on your Garmin watch. This will remove any Connect IQ app and remove all your personal settings. Then, reinstall the Stryd Zones Connect IQ app using the Connect IQ app on your phone.

    3. Do not install any other data fields other than Stryd Zones. Other Connect IQ apps and data fields may interfere with Stryd Zones

    4. If you cannot restore factory defaults on your watch, uninstall all Connect IQ apps from Garmin Express, and reinstall Stryd Zones.

    I don't like it, but clearly the stance here is that if Garmin won't make it possible for data fields to play nice with each other under all circumstances, the simplest choice is to tell people not to use other data fields.

    I think some other apps also have a "reduced FIT field" mode in settings. This probably wouldn't work for Stryd as Stryd always wants to record a full set of metrics so they can do a bunch of analysis in their app. (This is one of the reasons why the Stryd app ignores power recorded with other apps, even when you try to make it look similar to the power that stryd works -- bc other apps won't record all of those other metrics.)

  • I don't think Garmin should try to guess how many fields/bytes each DF might use. What I would expect however is that when they detect a problem in real time (which they clearly do, 'cause at least 1 CIQ DF crashes when it happens) they would do something "better". Ideally it could be some human readable message to the user (maybe as a notification, so the user could read it, acknowledge it, and then make their decisions and remove some fit fields (if possible from settings) or remove some of the data fields from the activity), and maybe even changing the not so useful System Error and Out of memory Error to some more developer friendly exceptions (i.e: Too Many Fit Fields Exception and Too Much Fit Data Exception) and most importantly make them catchable!

  • Yeah given that the design is the way it is (and probably won't change), it would be nice if the associated errors were catchable.

    However, this would still present a problem to app devs, who would have to communicate to end users that there is a problem and somehow guide them to solve it ("uninstall that other CIQ data field!"). Yeah it's not too different than the current situation.

    At least if FIT field errors were catchable then devs would have more choices.

  • I would prefer to be able to catch the error, and instead of the "crash icon" display a short error message, even if it's as stupid as "Error 3, read app description" where it could be explained, and then I would guess that more users that experience this issue would actually read and maybe even understand it (And maybe some of them be less "angry" with your app)

    In certain DFs (if I have the code memory to add code like this) I might even do the following: set up the fit fields according to order of preference (even if it's hard coded by me) and start with the most important (i.e: ANT_HR), then go to less important values (average, min, max - though I realize that the example is not perfect, as HR is type:record, and avg,min,max are either type:lap, and/or: type:session, but you get the idea), and do each in a try/catch, so I am able to create the more important ones and not crash.

    Or there could be even a NEW function added to newer SDKs (not the best, but more realistic): canCreateField(... the same parameters as createField) as Boolean. This way at least for newer watches we could improve the user experience...

  • Or there could be even a NEW function added to newer SDKs (not the best, but more realistic): canCreateField(... the same parameters as createField) as Boolean. This way at least for newer watches we could improve the user experience...

    This function as described could lead to a race condition, depending on how CIQ data fields are “scheduled”. (Yeah I know there’s no processes, threads, or full-fledged OS, so it’s likely that the other field can’t and won’t interrupt your field’s current “execution context”. But who knows.)

    In any case, you could implement a similar concept — tryCreateField(), which tries to create a field and fails gracefully — if Garmin would make the FIT field errors catchable. 

    I don’t have a lot of confidence that either of those things will happen (new function or catchable errors.) As someone else would say, Garmin has known about this problem for years and has done nothing about it…. And as I would say, Garmin (as a whole) doesn’t really act like they care that much about CIQ anyway. (If you look at the support pages for companies like Stryd that have both Apple Watch and CIQ apps, they basically gush about how Apple is so easy while profusely apologizing for the terrible CIQ user experience.)

  • Yes, I could see that race condition in my test: https://forums.garmin.com/developer/connect-iq/f/discussion/7961/overview-of-connect-iq-apps-accompanied-with-source-code#pifragment-1298=10

    In the test I made the 1st CIQ DF (that was higher in the layout) was OK, and the 2nd crashed, when I switched then it was again the one that was lower that crashed. Though maybe this is not 100% deterministic.

    Anyway, of course you're right, that's why the better solution would be to display a full screen message to the user explainig the problem, and maybe even giving a list of the data fields that currently created fit fields (it is possible that another DF will only create a fit field later, let's say when start button is pressed. I had to move the creation to the constructor, because when I only created the fit fields in the view (after getInitialView is called) then always "my" DF crashed, even when it was above the other :) Better to fail as early as possible I guess...

    Although in Garmin 27.0 they could even get a list of all the DF-s that call createField in their code :) But we're only at Garmin 1.0...

  • To be clear, by race condition, I meant:

    - two apps have an initialize() function (for the appbase or initial view class) that has both the hypothetical “canCreateField()” and “createField()” calls in consecutive order (with createField() being called conditionally based on the return value of the other function). (iirc it’s not possible to call createField() after the initial view is initialized)

    - if Garmin watches had a real OS with processes, then it would be hypothetically possible to have this execution order:

    app1 - canCreateField() (returns true)

    app2 - canCreateField() (returns true)

    app1 - createField() (success :D)

    app2 - createField() (crash :/)

    However my guess is that this is impossible, as I doubt that one CIQ data field can interrupt another CIQ data field’s “execution context” (so to speak). Meaning that when the firmware triggers some facet of the app lifecycle (e.g. app onStart, getInitialView, onUpdate for data fields, compute for simple data fields), then that facet will probably run to completion without interruption from the other CIQ data field

    I could be wrong ofc, but that’s my guess considering that there are no processes or threads (and given the fact that an app watchdog timer is considered to be necessary in the first place.) 

    The race condition you described can be explained by two data fields being initialized in a certain order (not necessarily interrupting each other.)

    Anyway, whether or not my race condition is possible, I kinda doubt Garmin would implement canCreateField() and I would argue that tryCreateField() would be better either way. It’s a moot point since I doubt either function will ever exist.

  • And then one that can be a bit more useful:

    - add settings for each of my fit fields to be able to enable/disable them separately, and by that hopefully users with crash (and who read the description...) will be able to keep the more important fit fields while giving up on the less important => adds lot of code

    This appears to be the best that we can do. Unless we can somehow prevent app crashes and predict app behavior, the whole FIT contribution feature should be considered experimental and never be enabled by default. I ended up with the same - just added a single app setting to enable FIT contribution, labeled with a warning message that this option may cause an app crash.