Value 'onSettingsChanged' not available in all function scopes.

I have a datafield. I am trying to add a background process to download some json file from the web. (I know new devices can do it in the foreground, but I also want it to work for older devices).

So I added (:background) annotation to the class of MyApp:

import Toybox.Application;
import Toybox.Lang;
import Toybox.WatchUi;

(:background)
class MyApp extends Application.AppBase {
    hidden var view as MyView?;

    function initialize() {
        AppBase.initialize();
    }

    (:typecheck(false))
    function getInitialView() as Array<Views or InputDelegates>? {
        self.view = new MyView();
        return [ self.view ] as Array<Views or InputDelegates>;
    }

    (:foreground)
    function onSettingsChanged() as Void {
        if (view != null && !isRunningInBackground()) {
            view.onSettingsChanged(SETTINGS_CHANGED_FROM_CONNECT_IQ);
        }
    }
}

(:no_ciq_2_4_0, :background)
function isRunningInBackground() as Boolean {
    try {
        var app = getApp();
        app.setProperty(PROPERTY_NON_EXSISTANT, 1);
        app.deleteProperty(PROPERTY_NON_EXSISTANT);
        return false;
    } catch (ex) {
        return true;
    }
}
(:ciq_2_4_0, :background)
function isRunningInBackground() as Boolean {
    try {
        Properties.setValue(PROPERTY_EXSISTANT, false);
        return false;
    } catch (ex) {
        return true;
    }
}

Until now in onSettingsChanged it called view.onSettingsChanged which was always set (IMHO), but now when the background app will run there won't be a view obviously.

The (:foreground) annotation doesn't help, I guess the whole class is in the background code, so if the onSettingsChanged method is overridden then it calls it. I get the following compilation errors. How can I make this compile?

ERROR: fr955: MyApp.mc:68,12: Value 'onSettingsChanged' not available in all function scopes.
ERROR: fr955: MyApp.mc:68,12: Cannot find symbol ':onSettingsChanged' on type 'Null'.
ERROR: fr955: MyApp.mc:68,12: Value 'SETTINGS_CHANGED_FROM_CONNECT_IQ' not available in all function scopes.

The 2nd error is just a bug in the compiler, as it should know that view isn't null, but what can I do if I don't want this code to run at all if the background?

Top Replies

All Replies

  • AppBase is always run in the BG. No need for the annotation. But doesn't hurt.

    I didn't think you could annotate individual functions to ONLY run in the foreground within a class designated to run in the background. I've used a variable (inBackground) to control this. If that works, that is really helpful.

        function onSettingsChanged() {
            if (inBackground == false) {  GetSettings.getSettings();  }
        }
  • That doesn't help. It's the same I tried with the !isRunningInBackground(), but the problem is that it doesn't even compile. I even tried to add to the if: && view has :onSettingsChanged, but it doesn't help either (why did I think it would if the compiler doesn't even know that view isn't null...)

  • Dumb question, but do you have getServiceDelegate() function overriden in your class? My first assumption is that the background instance of the app is initialized and started in a wrong way and behaves like a semi-foreground app, i.e. getInitialView() is never invoked but onSettingsChanged() is.

    In my own app with background process I have no null or "isBackground" checks inside the onSettingsChanged() function at all and it works without issues

  • Not to state the obvious, but the "foreground annotation" doesn't help bc foreground isn't a built-in CIQ annotation which does what you want it to do (exclude code from the background process). Someone (I think jim_m_58) explained that the PRG is laid out in such a way that it's possible for the background process to only include the background code, but the foreground process has to include all of the code. I think maybe all the FG-only code and data is at the end of the PRG.

    e.g. PRGs might look like this:

    -------------------
    |Header           |
    -----------------
    -
    |...              |
    ------------------
    |BG/FG code/data  |
    ------------------
    |FG-only code/data|
    -------------------

    Anyway, I think the solution is to create a global function (lacking the background annotation) to handle the duties of onSettingsChanged(), then conditionally call that from MyApp.OnSettingsChanged() if the symbol exists.

    If you have lots of stuff that needs to be foreground only you can make it a class or a module if that would be cleaner.

    (:background)
    class MyApp extends Application.AppBase {
        // ...
        function onSettingsChanged() as Void {
            if ($ has :onSettingsChanged && view != null) {
                $.onSettingsChanged(view);
            }
        }
    }
    
    function onSettingsChanged(view as MyView) as Void {
        view.onSettingsChanged(SETTINGS_CHANGED_FROM_CONNECT_IQ);
    }

  • My problem is not when I run the app, it already fails to compile. I do have getServiceDelegate but I don't think it matters at all.

    I guess your onSettingsChanged works because it only calls code that itself is (explicitly or implicitly) annotated as :background. I haven't try this, but I'm pretty sure that if I added (:background) on MyView class, then it would work, but the whole point of BG is that there's not much memory, so I don't want to add my biggest class, that by definition won't be used in the BG app... There must be a way to make this work...

  • Ok gl.

    I actually wasn't able to recreate your exact problem, so it's possible I'm missing something here. It doesn't seem like there's anything wrong with your code to me. I tried CIQ SDK 7.1.1 (which I assume is what close to what you used, given the typecheck(false) annotations to deal with the latest type changes.)

  • A few things. 

    Your app can function without onSettingsChaned in the code.  The drawbacks are that the display might not update immediately, and it's less efficient in that each property needs to be read each time onUpdate is called.  When app settings were first introduced, the ObjectStore Sample was the sample to show how it works.  And there is no onSettingschanged in it.  For some reason, that sample is no longer in the 7.x.x SDKs, but check an older one.

    Also, since type checking was introduced, it's been good at flagging things it doesn't like, but in general may not really be a bug.  When in doubt, I turn off type checking and see what happens.

  • Your app can function without onSettingsChaned in the code.

    So basically your advice is if you can't get something working, don't use it?

    Also, since type checking was introduced, it's been good at flagging things it doesn't like, but in general may not really be a bug.  When in doubt, I turn off type checking and see what happens.

    You always advise people to turn off type checking if they run into compile-time errors (which may or may not be related to type checking.)

    This type of attitude defeats the whole purpose of having type checking in the first place. Ppl post about bugs every now and then which could've been avoided with type checking.

    A better approach would be to:

    - write code that works well with type checking

    - if the type checker incorrectly flags good code, ask Garmin to fix it

    - similarly, if the API is incorrectly typed (as was the case with weather-related fields), ask Garmin to fix it

  • I use onSettingsChanged in all my apps with app settings.  All different apps types (well I don't have any music apps!), with and without glance mode or backgrounding.  

    I'm just trying to understand the issue as it's nothing I've ever seen.  Because I've used app-settings for years, I'm just trying to understand why things work fine for me, and offering work arounds.