DF optimization: should I check every time for info has :currentHeartRate in compute?

Currently my DF's cumpute() starts with:

var hr = (info != null && info has :currentHeartRate && info.currentHeartRate != null)
  ? info.currentHeartRate
  : "--";

Should I check info has :currentHeartRate in every call to compute() or can I cache it:

if (mHasCurrentHR == null && info != null) {
  mHasCurrentHR = info has :currentHeartRate;
}
var hr = mHasCurrentHR == true && info != null && info.currentHeartRate != null ? info.currentHeartRate : "--";

Or in other words: does info always have :currentHeartRate on a specific device, even when there won't be HR for a while or can it "surprise me" that sometimes it'll have a non-null info that doesn't even has a null info.currentHeartRate?

Or do I even need to bother with the has? Can I just write:
var hr = (info != null && info.currentHeartRate != null)
  ? info.currentHeartRate
  : "--";

Or even better: can I move this to initialize somehow?
function initialize() {
    var info = ActivityMonitor.getInfo();
    mHasCurrentHR = info has :currentHeartRate;
}
Can I be sure that the info I get this way indicates regarding has :currentHeartRate what will the info sent to compute(info) be like?

  • No, at most you need to do the "has" in initialize and set a boolean that's used later.  "has" is a dictionary look up and more expensive than just checking a boolean

    But the reality is, you don't really need it at all, as every device will provide currentHeartRate.  You do want to null check it though.

  • I don't remember why I added the has check, I think it was because in some edge devices it wasn't there. I can try to see it in the simulator...

  • And Edge will have a valid value if a HRM is connected,  Just check for null.

  • To clarify, "has" is actually a kind of runtime type check.

    https://developer.garmin.com/connect-iq/reference-guides/monkey-c-reference/#instanceofandhas

    Monkey C provides two operators to do runtime type checking that need some special attention: instanceof and has. Monkey C’s object-oriented design patterns in conjunction with the has and instanceof operator enables software that has implementations for many devices in a single code base.

    ...

    The has operator checks whether a given object has a particular symbol, which may be a public method, instance variable, or even a class definition or module. For example, accelerometer data is available in Sensor.Info, but not all products have an accelerometer. Attempting to use this data on certain products may cause the app to crash with a Symbol Not Found error. To avoid this, the has operator can be used to check for accelerometer support:

    (Looking up a symbol in an object is not the same as looking up a key in a dictionary.)

    EDIT: I guessed wrong:

    Since types can't change at runtime (without changing the device or its software), it's not necessary to execute a given "has" statement more than once per app.

  • (Looking up a symbol in an object is not the same as looking up a key in a dictionary.)

    The symbols are internally in a dictionary.  So when you has check a symbol, you are in fact looking in a dictionary.  Look back in some of the really old posts here.

  • The symbols are internally in a dictionary.  So when you has check a symbol, you are in fact looking in a dictionary.  Look back in some of the really old posts here.

    Thanks! Sorry, I misinterpreted your statement -- I thought you were talking about the semantics of has and not the implementation.

  • everything depend on system. it would be fine to know that something HAS something in initialize but it can be problem sometimes, example

    1. system returns data in dictionary and you don't know surely if it has all keys - no data, no key

    2. I can seen that system doesn't load all 'services' before WF starts after reboot of watch so e.g. HAS sensor returns false on beginning..

  • 2. I can seen that system doesn't load all 'services' before WF starts after reboot of watch so e.g. HAS sensor returns false on beginning..

    Very interesting, I would not have expected that. Thanks! I edited my previous comment to strike out misinformation lol.

  • I'm surprised, but this is Garmin, so we shouldn't be surprised... Though there is a difference (at least how I see it) between returning a Type vs a Dictionary. If the docs tells you that Activity.getActivityInfo return Activity.Info, then it's a Type and not a dictionary, so I can hardly see how the structure can change unless you upgrade the FW. But again... who knows.

    can you be more specific in your example, when you couldn't cache the results of X has Y in a constructor and had to check it later (or every time)?

  • hough there is a difference (at least how I see it) between returning a Type vs a Dictionary. If the docs tells you that Activity.getActivityInfo return Activity.Info, then it's a Type and not a dictionary, so I can hardly see how the structure can change unless you upgrade the FW. But again... who knows.

    I agree, but when Jim said "it's a dictionary lookup" he meant the implementation of "has" uses a dictionary lookup, not that an instance of something like Activity.Info is literally a dictionary that can be accessed in the same way. I assumed that "has" had some way of checking a type that literally could not change without upgrading firmware (as you said), which is why I made the guess above.

    Given the "dictionary lookup" implementation, it makes sense that "has' could return different values at runtime, but it seems less than ideal to me.