how can i share view code for both datafield and watch-app

I would like to share my view code between a watch-app and a datafield. 

I can't figure out how to:

 1) determine at run time if I am running as a datafield or a watch-app, is there some mechanism to do so?

 2) It looks like the initial view either has to extend a WatchUI View or Datafield, now sure how to do both.  Can I just extend View and at runtime during initialization either call DataField.initialize or View.Initialize (again, then I would need to figure out how to determine what I am at run time).

  • It isn't going to be ideal to determine at runtime if your app is a datafield or a watch-app, and I'm not sure I understand how this will be helpful. At some point you will not be able to share code. You've discovered the datafield app type requires your view extend DataField, so that is a pretty clear line in the sand.

    If all you want to do is share code while fitting into the framework by using the right view class, the easiest thing to do is to extract the code that you want to reuse, and then reference them from the derived View or DataField. Something like this...

    // Common.mc: shared by watch-app and datafield
    class MyCommon
    {
        // all shared data goes here
    
        function initialize() {
        }
    
        // all shared functions go here
    }
    
    
    //
    //
    //
    
    // MyWatchApp.mc: used only by the watch app
    class MyWatchApp extends Application.AppBase
    {
        function initialize() {
            AppBase.initialize();
        }
        
        function getInitialView() {
            var common = new Common();
            return [ new MyWatchAppView(common) ];
        }
    }
    
    // MyWatchAppView.mc: use only by the watch-app
    class MyWatchAppView extends WatchUi.View
    {
        hidden var _common;
        
        function initialize(common) {
            View.initalize();
            _common = common;
        }
        
        // use _common
    }
    
    //
    //
    //
    
    // MyDataFieldApp.mc: used only by the datafield app
    class MyDataFieldApp extends Application.AppBase
    {
        function initialize() {
            AppBase.initialize();
        }
        
        function getInitialView() {
            var common = new Common();
            return [ new MyDataFieldView(common) ];
        }
    }
    
    // MyDataField.mc: used only by the datafield app
    class MyDataField extends WatchUi.DataField
    {
        hidden var _common;
        
        function initialize(common) {
            DataField.initalize();
            _common = common;
        }
        
        // use _common
    }
    

    Typically this shared class would be the Model in a Model-View-Controller implementation, but you could also do something where it was just common view code for displaying data.

  •  2) It looks like the initial view either has to extend a WatchUI View or Datafield, now sure how to do both.  Can I just extend View and at runtime during initialization either call DataField.initialize or View.Initialize (again, then I would need to figure out how to determine what I am at run time).

    You can't have a single application that is both a watch-app and a datafield and changes its behavior at runtime based on how it is loaded. When your app is compiled the manifest.xml specifies the app type. A compiled .PRG can only be on application type and each application must have a unique manifest identifier.

    As shown above, you can certainly share code. You can do this with a barrel, a barrel project, revision control system (e.g., a git submodule), or by just copying files around on your system. Beyond the shared code there will be code/files that are not going to be sharable.

  • As , there's no such thing as a "hybrid" .prg that can be both a DF and a devices app, so no need to do any runtime logic.  The code is built as either a DF or a device app - different app types in the manifest, as well as different mainifest IDs.

    Something that may of be of concern is that things are not shared - Storage or settings - the device app can't save something that's available to the DF and v.v.  In the same way, if you use app settings, they are separate, so if a user chances something for the device app, they must also change it for the DF.

    There's also some really significant differences between how a DF and a device app work.  In a device app, there's a whole bunch of things you need to deal with, like starting/stopping/pausing the recording, enabling the sensors, etc.  In a DF, compute gets called every second, and when visible, onUpdate is also called every second.  In a device app, you need a timer to trigger things like this.  There's also the whole max size.  DF's are typically much low than device apps.

    When something is a DF, if a user wants to see other data, they can just add another DF for that, but in a device app, your apps data is all they can see, so there may be things your device app needs to display that wouldn't be needed in a DF, so you probably wouldn't use the same onUpdate() code, as a device app can also have multiple screens.

    When it gets down to it, your may find the amount of shared code between a DF and device app isn't that much

  • Perhaps I wasn't clear.  Right now I am weighing the pros and cons and developing either a device field or an app.  I would like to just be able to change the manifest file, and at run time see what type I am and then execute a slightly different code path.  My question was mostly about if there is anyone to do that, or do I have to make syntactical changes in my View/DataField subclass (besides changing the manifest).

  • TL;DR since you are effectively going to have 2 different projects/apps/PRGs one way or another, you can implement your own mechanism for determining whether it's a datafield or device app. The easiest way to do so would be to hardcode the differing logic in whichever source files / classes / functions are unique to either to datafield or device app. If you insist on sharing *all* the source files, you could just have one field whose value changes depending on whether you built a data field or device app (using jungle exclusions), although I don't really recommend this approach.

    As already pointed out, you can't really share all of your code, although you can have some common code (e.g. in the form of a library). You are going to have two different projects (or one project with two different manifest files, if you prefer), and you are also going to have two different files for implementing your AppBase class -- one will return an initial view that extends WatchUi.View, while the other will return an initial view that extends WatchUi.DataField.

    If you're considering implementing all of this in a single codebase and simply choosing which view to return at "runtime" as opposed to having two sets of AppBase classes which are selected at compile time, you could do it if you really wanted to, but it would require two different monkey.jungle files:

    App monkey.jungle:

    base.excludeAnnotations = datafield

    Datafield monkey.jungle

    base.excludeAnnotations = app

    App implementation:

    class MyApp extends Application.AppBase {
      	(:datafield) const isApp = false;
    	(:app) const isApp = true;
    
        function getInitialView() {
            if (isApp) {
                return [ new MyAppView(), MyAppDelegate() ];
            } else {
                return [ new MyDataFieldView() ];
            }
        }
    
    }

    TBH I don't see a lot of value doing this, since as pointed out, a device app and datafield are very different. *Something* needs to be determined at compile time, and doing it this way just moves the line in the sand to a slightly different place -- instead of including different source files in different projects, you're including all the source in the same project and using a compile-time exclusion to determine which code gets run. This only serves to make the code/PRG a bit bigger.

    I have done something similar with a widget and device app, but only because they shared the same view class whose behaviour needs to change depending on which type it is. And I still have two separate projects, and two separate source files for my AppBase class. I do have a run-time flag similar to isApp above, but only because there's certain logic differing between app and widget which I wanted to implement at a more granular level than entire classes, functions or fields (which is the level that exclusions work at).