Does System 6 simulator glance behaviour match hardware or is it broken?

I'm doing a significant  update on an app that I first released about 18 months ago and I've hit a problem with System 6 devices, so Fenix 6 and similar. I have tried searching for this but not found anything directly addressing it.

This was with SDK 8.4.1 and also with the latest, 9.1.0.

With System 5,

  • One instance of the App is created.
  • getGlanceView is called once.
  • The App instance (or view) makes a web request to fetch a json file.
  • The onLayout and onUpdate methods are called.
  • When the web request returns, the callback method requests an update.
  • onUpdate method is called again and the new data is displayed. 
  • Closing the app via the debug controls causes a clean exit.

With System 6:

  • Three instances are created.
  • getGlanceView called for each.
  • Each makes the web request.
  • onLayout and onUpdate are called on the last instance.
  • The web request is only returned to the first instance.
  • Web request never calls the callback for the other instances, neither fail or success
  • The first instance requests an update but nothing happens.
  • Closing the app via debug controls causes a "Failed to terminate app:Timeout"


Here is a minimal reproducible example (can't attach, .mc files are not allowed):
It uses a random number string to identify the app instances.

 

import Toybox.Application;
import Toybox.System;
import Toybox.Lang;
import Toybox.WatchUi;
import Toybox.Graphics;

function getApp() as mreApp {
    return Application.getApp() as mreApp;
}

(:glance)
class mreApp extends Application.AppBase {

    var id as String = Math.rand().toString() as String;
    function initialize() {
        AppBase.initialize();
    } 


    function getInitialView() as [Views] or [Views, InputDelegates] { 
        System.println("App - getInitialView: "+id);
        return [new View()];
    }

    function getGlanceView() as [ GlanceView ] or [ GlanceView, GlanceViewDelegate ] or Null {     
        System.println("App - getGlanceView: "+id);

        //Position C Fetcher code here has same effect as Position A in the glance view - removed for clarity
        return[new mreGlanceLiteView(id)];
    }
}

(:glance)
class mreGlanceLiteView extends WatchUi.GlanceView {

    var id as String;  
    var fetcher as Fetcher or Null;
    function initialize(_id as String) {
        GlanceView.initialize(); 
        id = _id;
        fetcher = new Fetcher(id);
        
        //Position A
        fetcher.fetch(); //<- this request is successful for the first instance only
    }

    function onLayout(dc as Dc) as Void { 
        System.println("Glance - onLayout: "+id);
        
        //Position B
        //fetcher.fetch();//<- this request never completes (onReceiveStatus is never called)
    }

    function onUpdate(dc as Dc) as Void {
        System.println("Glance - onUpdate: "+id);
    }
}

(:glance)
class Fetcher{
    var id as String;  
    function initialize(_id as String) {
        id = _id;
    }
    function fetch() as Void {
        System.println("makeWebRequest called: "+id);
        
        Communications.makeWebRequest(
                    "https://jsonlint.com/datasets/lorem-ipsum.json", 
                    {}, 
                    {                                             
                        :method => Communications.HTTP_REQUEST_METHOD_GET, 
                        :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
                    }, 
                    method(:onReceiveStatus));   
    }

    function onReceiveStatus(responseCode as Number , data as Dictionary?) as Void {
        if (responseCode == 200) {
            System.println("makeWebRequest successful requestUpdate id: "+id);
            WatchUi.requestUpdate();
        } else {
            System.println("makeWebRequest Error: " + responseCode + " id: "+id); 
        }
    }
}


Output for system 5 (Forerunner 945):

App - getGlanceView: 1152874090
makeWebRequest called: 1152874090
Glance - onLayout: 1152874090
Glance - onUpdate: 1152874090
makeWebRequest successful requestUpdate id: 1152874090
Glance - onUpdate: 1152874090


Output for system 6 (Fenix 6):


App - getGlanceView: 1152873265
makeWebRequest called: 1152873265
App - getGlanceView: 1252220708
makeWebRequest called: 1252220708
App - getGlanceView: 1956668138
makeWebRequest called: 1956668138
Glance - onLayout: 1956668138
Glance - onUpdate: 1956668138
makeWebRequest successful requestUpdate id: 1152873265
Failed to terminate app: Timeout  
 

To be clear, the three instances would not be a problem if it the glance just had to draw something. The issue is that the instance that makes the web request is different to the instance that draws the view so there is no visual update. I tried calling the webrequest within onLayout, hence skipping the first 2 instances, but the callback is never called. On system 5, the web request in onLayout works fine.

My question is, is this a bug in the simulator or is this actually how system 6 devices behave? If it is how the device works, is there a workaround to get the 3rd instance to update the display from the web request callback on the first instance?

I don't remember having this problem in the previous round of development but maybe I missed it. I should add, the main app works normally and I haven't noticed any similar issues. In the actual app, the fetched json data is persisted in storage and the main app uses is without issue.

Also, at this stage, with memory constraints, I have basically 3 separate code bases, system 3 and 4 work fine because there are no glances, system 5 and 6 share a code base but work differently as described. System 7 and 8 do significantly more and work fine with no problems in glances or the main app. 

  • Can you try this after deleting all apps from the simulator, and resetting the simulator (both in the File menu), and closing the simulator?

  • See https://developer.garmin.com/connect-iq/core-topics/glances/

    "Glance Lifecycle"

    (for background updates)

    Devices that have less memory2 will start the app only when the system deems it appropriate, and calls to WatchUi.requestUpdate() will have no effect. Such a device could update their glance view when it becomes visible (activated) and at least 30 seconds since last update.

    ------------------------------

    in the devices you can check if the device is one where requestUpdae is ignored.

    Here's the Fenix 6 for example:

    "liveUpdates": false

    and the Fenix 7:

    "liveUpdates": true

  • Deleting all apps, resetting the simulator and closing it before running the code doesn't change anything. Still 3 instances created. Last instance has GUI drawn, only first instance can make a webRequest.

  • The sinulator.json does indeed have live updates false for fenix 6 (system 6) and true for fr945 (system 5) which is perhaps surprising. I had assumed they would have the same behaviour since they have similar memory constraints. So that would explain the lack of UI update.

    However, that didn't explain why the web request only completes when called from the first instance of the three. On further investigation, it appears the first instance is kept alive, the second instance is stopped immediately before doing anything and the third instance is stopped immediately after drawing the UI.This could explain the lack of web request return.

    So the first instance is kept alive and this is true even if no web request is made. Other instances are created on the fly.

    The final part of the problem is that the simulator can't be closed properly from the debug controls, again even if no webrequest is made, it displays the failed to terminiate message. I'm still not clear if this is safe to ignore?

    So to summarise, the remaining question:, is the simulator live update setting the same as on devices? And is it safe to ignore the failed to terminate message or is that something that could cause a freeze or memory/battery issue on an actual device?