WatchFace crashes when my phone's bluetooth is off and while I'm sleeping

Hi there lads,

I'm testing my WatchFace very hard for several days and nights, on my own fenix6xpro watch. The performance of the watch in my eyes is excellent, but it's been two times that I've disconnected the bluetooth from my phone before going to sleep at night, and when I wake up, I see that the watch has one of the default Garmin WatchFace running.

When I go to all the WatchFaces on the watch, I see that mine has the warning triangle, and when I select apply, it doesn't work.

Once I turn the bluetooth back on on my phone, and apply my WatchFace again, then it works again.

I'm trying to reproduce the error in the simulator but I can't do it. I have try catch in my functions, and the total execution time when it's in low battery mode is about 21 seconds, I know that maybe I should lower that time more, but I think it's something else.

What could be going on here? Any suggestions?

Best regards,

/Juan

  • Have you looked at the CIQ_LOG files in garmin/apps/logs?  This give you info and a stack trace of crashes.

  • Nop, I haven't, didn't know that was even possible, I'll take a look to see what I can find. Thank you very much Jim.

  • If your watch face is a sideload, build it as a debug build and the stack trace is easy to understand.  If it's a release build see this: https://forums.garmin.com/developer/connect-iq/f/discussion/231129/so-you-have-a-ciq_log-file-but-all-you-see-is-pc-without-a-friendly-stack-trace---what-to-do

  • Nice article btw! Thanks for sharing.

    Ok I got the logs and yes, there is an error indeed. Here it is:

    Error: Unexpected Type Error
    Details: 'Failed invoking <symbol>'
    Time: 2024-07-25T05:30:58Z
    Part-Number: 006-B3291-00
    Firmware-Version: '27.00'
    Language-Code: spa
    ConnectIQ-Version: 5.0.0
    Filename: MyWatchFace
    Appname: MyWatchFace
    Stack: 
      - pc: 0x10001b6b
        File: /Users/juan/Documents/Repos/MyWatchFace/source/MyWatchFaceView.mc
        Line: 692
        Function: drawWeatherInfo
      - pc: 0x10001f56
        File: /Users/juan/Documents/Repos/MyWatchFace/source/MyWatchFaceView.mc
        Line: 64
        Function: onUpdate


    And here is how I'm doing it:
    function getCurrentConditions() {
        if (Toybox has :Weather) {
            return Toybox.Weather.getCurrentConditions();
        }
    
        return null;
    }
    
     hidden function drawWeatherInfo() {
        try {
            var conditions = getCurrentConditions();
            
            if (conditions == null) {
                return;
            }
    
            var temperature = conditions.temperature;
            var temperatureLabel = View.findDrawableById("TemperatureLabel") as Text;
            temperatureLabel.setColor(temperatureLabelColor);
            temperatureLabel.setText(temperature.format("%d") + "°");
        } catch (ex) {
            System.print(ex);
        }
    }


    As you can see there is nothing weird here, it is a basic code.

    Anyway, I'll do what you suggested and build the app as debug, to get more info in the stack trace.

    Thanks again for the help.

    Br,

    /Juan

  • What's at the line numbers in your code referenced in the stack trace?

    Note - anything in conditions can be null, even if conditions itself isn't null.  The one I see most often is "feelsLikeTemperature"

    Notice in the api doc it has (for example) 

    feelsLikeTemperature as Lang.Float or Null

    The "or Null" means you need to null check the data.

  • Yeah, the crash here is probably due to CurrentConditions.temperature having a null value when you don't have a network connection.

    The fix, as jim_m_58 said, is to null check the temperature value.

    Couple of notes:

    - There was an API/documentation error in older SDKs. The bug is that the types for various CurrentConditions temperature-related fields (such as temperature) used to be specified as Number, but in fact, they should've been specified as either Float or Numeric (which is Number, Long, Float or Double). In this case, temperature is Numeric, not Number. This is because some devices would return temperature as an integer (e.g. Number) but other devices would return temperature as a Float.

    Note that the behavior of the devices hasn't changed, but the types used to be wrong and now they're correct.

    This means you may want to explicitly round temperature, as calling format("%d") on a Float/Double will truncate it (round towards 0), rather than rounding to the nearest integer. In other words, if you receive a value like 19.9 for temperature, you probably want to round it to 20 for display purposes, rather than truncating to 19.

    - Note that enabling type checking (at a certain level) would've caused a compile warning or error with the code as written

    For example, the following code will produce a warning at type check level 2 / informative, and an error at level 3 / strict (the default level is 1 / gradual):

    var conditions = Weather.getCurrentConditions();
    if (conditions != null) {
        var temperature = conditions.temperature;
        System.println(temperature.format("%d")); // "Cannot find symbol ':format' on type 'Null'."
    }
    

    This demonstrates the value of enabling type checking (even at a higher-than-default level.)

    Having said all of that, I would rewrite your code as follows (or something similar):

    if (conditions == null) {
        return; // do you want to clear the temperature field in this case?
    }
    
    var temperature = conditions.temperature;
    if (temperature == null) {
        return; // do you want to clear the temperature field in this case?
    }
    
    const temperatureText = Math.round(temperature).toNumber().format("%d");
    
    var temperatureLabel = View.findDrawableById("TemperatureLabel") as Text;
    temperatureLabel.setColor(temperatureLabelColor);
    temperatureLabel.setText(temperatureText + "°");

  • Thank you for the detailed explanation and the excellent suggestion,. I'll definitely incorporate the null check and rounding adjustments for the temperature value as you've recommended.

    However, I'm curious why my watch face is still crashing even though I have a try-catch block around the code. Could you shed some light on this behavior?

    Also, a big thanks to for the previous guidance—much appreciated!

    I'll double-check all my code to cover any missing null checks.

    /Juan

  • try/catch is only for certain things, and not this.  You need the null check.

    And as far as handling the data like temperature, if what you see is a Number for a Float, if you do a proper conversion to F, the result will be a float.

    And a simpler way to handle this is to just use

    temperature.format("%0.1f"),

    where you just show  it with 1 decimal point, so you don't need to do rounding in the integer part.  The decimal point is nice  - right now it's 109.9 so easy to see it's almost 110F!

  • Got it, thanks for the clarification on try/catch and null checks. I'll implement the null check as suggested.

    Nice tip on using .format("%0.1f") to handle the temperature display more effectively. I'll give it a shot to see how it looks.

    Thanks again for your insights!

  • Yes, in most of my stuff that shows temperature, I include one decimal point,  And when it comes to space to display it, don't forget about handling temps over 99F!