Persistent "Cannot override getInitialView with different return type" Error (fr955 / SDK 8.1.1)

Hi everyone,

I'm developing a simple application/widget and I'm stuck on a persistent compilation error when targeting the Forerunner 955 (fr955) using Connect IQ SDK version 8.1.1.

The Error:

The compiler throws the following error on the getInitialView function definition line (line 147 in my current file):

ERROR: fr955: Cannot override '$.Toybox.Application.AppBase.getInitialView' with a different return type. Object of type '$.Toybox.Lang.Array<...>' does not match return type 'PolyType<$.Toybox.Lang.Array[$.Toybox.WatchUi.Views] or $.Toybox.Lang.Array[$.Toybox.WatchUi.Views, $.Toybox.WatchUi.InputDelegates]>'.

My Code:

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

import Toybox.Application;
import Toybox.WatchUi;
// ... other imports ...
import Toybox.Lang;
import Toybox.System;

// --- View Class (PointCalView) is defined above, seems OK ---
// class PointCalView extends WatchUi.View { ... }

// --- Application Class ---
class PointCalApp extends Application.AppBase {

// Member variable to hold the single view instance
var mainView as PointCalView?;

function initialize() {
AppBase.initialize();
mainView = null;
System.println("PointCalApp initialized.");
}

// getInitialView function causing the error (around line 147)
// Note: No explicit return type hint on the signature
function getInitialView() {
if (mainView == null) {
System.println("Creating mainView instance.");
mainView = new PointCalView();
} else {
System.println("Returning existing mainView instance.");
}
// Note: No explicit cast on the return value
return [ mainView ];
}

// onStart, onResponse, onStop methods are here...
// They seem to work fine based on console logs (API call successful,
// data parsed, mainView.updatePoints called correctly on the stored instance)

// ... (onStart, onResponse, onStop code can be included if needed, but the error points to getInitialView)
}

// --- Entry Point ---
function main() as Application.AppBase {
var app = new PointCalApp();
return app;
}

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

Here's the relevant part of my PointCalApp.mc file, including the PointCalApp class and the getInitialView function:

Troubleshooting Done:

  • I have confirmed that the rest of the application logic (API calls, data parsing, updating the view's variables via mainView.updatePoints()) works correctly based on extensive console logging. The view variables do get the correct values.
  • I initially had explicit return type hints (as ...) on the getInitialView signature and/or casts (as ...) on the return statement, which also caused this error.
  • I have removed all explicit type hints and casts from getInitialView and its return statement, as shown in the code above.
  • I have performed clean builds (deleting the bin folder).
  • The error persists specifically for the fr955 target with SDK 8.1.1

My Question:

What could be causing this persistent return type mismatch for getInitialView on fr955 with this SDK version? What is the exact expected signature or return type structure that I should be using? Is there any known issue or workaround?

Thanks in advance for any help!

  • There's actually nothing "wrong" with your code as written, you've run into a Monkey C bug [or limitation].

    As flocsy's link explains, the return type for getInitialView() has indeed changed from Array<Views or InputDelegates>? to [Views] or [Views, InputDelegates]. (The latter types are a tuples - fixed-length arrays where each index has a specific type.)

    However, there's something else going on here that's preventing your code from working as written. After all:

    - the return type of getInitialView() doesn't need to be specified since it's an override (clearly the compiler knows what the return type is, as evidenced by the error message, and by the fact the hovering over the function name will show the return type)

    [however, if you *do* specify the return type, it naturally does have to match the actual return type of the method that's being overridden]

    - a cast on the return value shouldn't be necessary, as the return value *looks* to be the correct type already.

    You did not post your *exact* error message, but I bet it was this:

    Object of type '$.Toybox.Lang.Array[Null or $.PointCalView]' does not match return type 'PolyType<$.Toybox.Lang.Array[$.Toybox.WatchUi.Views] or $.Toybox.Lang.Array[$.Toybox.WatchUi.Views, $.Toybox.WatchUi.InputDelegates]>'.

    See, the problem is the compiler is not smart enough to realize that mainView is non-null when return [ mainView ] is executed.

    I think you'll find that if you hover over mainView on the same line, you'll see that the type checker erroneously thinks it can be null:

    There's several outstanding bugs where the compiler is unable to tell that a nullable variable's type is non-null (at a given point in the code). In some cases, including this case, adding an intermediate local variable is a workaround: https://forums.garmin.com/developer/connect-iq/i/bug-reports/type-narrowing-still-doesn-t-work-for-non-locals-e-g-global-variables-or-member-variables 

    [I think the reason this works is because local variable have mutable types, whereas non-local variables have fixed types. However, you'd think that the compiler would still be smart enough to know that a non-local variable has "narrower" type than usual in certain cases.]

    I think you'll find that the following rewrite of getInitialView() gets rid of the type check error. Note that no casts are required - personally I prefer to avoid type casts wherever possible in a type checking system like TypeScript or Monkey C where casts have no effect at runtime, as they only serve to circumvent the type checking system. Type casts are especially dangerous in Monkey C, as there are no safeguards to prevent casting between incompatible types.

    If you're wondering why it worked before, it's almost certainly because you had a type cast on the return value. Without the type cast, you would almost surely have to run into the exact same error (where the compiler thinks mainView can be null at a point where it can't be null)

  • Another way to get around the issue would be to declare mainView to be PointCalView [not nullable].

    After all, the fact that mainView is nullable means that whenever you need to actually access it, you either have to:

    - use a type cast [which is generally unsafe, as mentioned above]

    - use a null check [which is superfluous since you know that it really won't ever be null]

  • Hi everyone,

    Just wanted to follow up and say thank you FlowState for the insightful explanations!

    The issue is now resolved.

    The solution that worked for me, as suggested, was to make the mainView member variable in my AppBase subclass non-nullable and initialize it directly in the initialize() method of the PointCalApp class, rather than in getInitialView().


    class PointCalApp extends Application.AppBase {

    var mainView as PointCalView; // Non-nullable

    function initialize() {
    AppBase.initialize();
    mainView = new PointCalView(); // Initialized here
    System.println("PointCalApp initialized. mainView created.");
    }

    function getInitialView() {
    System.println("Returning mainView instance.");
    return [ mainView ]; // Now 'mainView' is guaranteed non-null
    }

    // ... rest of the class ...
    }

    This indeed fixed the Cannot override '$.Toybox.Application.AppBase.getInitialView' with a different return type error I was getting with SDK [Votre_Version_SDK, ex: 8.1.1] when targeting the Forerunner 955 (fr955). It seems the compiler's type inference for nullable member variables in the return [ mainView ] context was the core of the problem.

    Thanks again for the great community support!