How to define own map keys like :bluetooth, :wifi

Currently I define map keys like following:

static const KEY_X = "x";

But I saw that something like following is possible as well:

var isWifiConnected = connectionInfo[:wifi].state == System.CONNECTION_STATE_CONNECTED;

Is it possible to define such variables like the ":wifi" myself aswell?

Top Replies

All Replies

  • As this discussion is a little offtopic already (but interesting) I wonder if there is a post/readme about some best practice tipps regarding both memory usage and battery drain...

    Reading this post I'm really surprised that things I would have never thought of may be problems or at least unefficieant. E.g. that using Graphics.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER vs 5 can make a difference - I would assume the compiler optimises this away to the exact same output.

    That arrays are more efficiant than maps makes sense of course.

    Regarding caching fonts - I do draw rotated texts in my watchface and this only works if I load/unload fonts everytime I draw a letter because it's impossible to load all fonts (one font per 2.5 degree) at once and reuse them - any tipps for this use case?

  • In the case of things like TEXT_JUSTIFY_CENTER|TEXT_JUSTIFY_VCENTER vs 5, it also impacts readability, and is just a few bytes.  I never like being at a point where I'm scrounging for a few bytes. You can probably make that up and more by finding ways to make your code a bit tighter.

    There's no real post with tips/best practices, etc. I gave a short talk at the Summit a couple years back on the topic, but to this day, I'm not sure what I said, and may have just been rambling!  Slight smile One of the things I did talk about was testing.  Test, test, and test some more, and make sure you include that things you yourself wouldn't do such as turning activity tracking off in the sim and see what happens, or set the floors goal to 0, etc.

    Your case with the rotated fonts is an example where this can vary by app.

    One thing I'd consider there (without seeing your code), is maybe look at what characters you need.  For example, if you never need a "z", filter that out, or if you only need some characters at certain angles, don't include them for all angles. (no need to change the font, just the filter)

  • As this discussion is a little offtopic already (but interesting) I wonder if there is a post/readme about some best practice tipps regarding both memory usage and battery drain...

    I apologize in advance for this lengthy and offtopic reply.

    This topic has come up several times in the past couple of years, actually. Sorry for posting this here instead of linking to an existing topic, but I just don't have a link handy (especially since the not-so-recent forum upgrade broken all the existing links).

    TL;DR - All of the nice Monkey C language features beyond the absolute bare bones, such as symbols / enums, classes, dictionaries, properties, resources, etc. come with a certain memory cost, which can be very apparent when you are dealing with an app that may have as little as 16 KB of available memory


    I can only speak for myself, but when developing CIQ datafields that can have as little as 16 KB of available RAM for old devices and 32 KB for newer midrange devices, I want to save every byte possible. I've had situations where saving 20-50 bytes allowed me to add a whole new *feature* to a data field. Then again, I don't have any super popular apps, so maybe I'm not the best person to give advice. ¯\_(ツ)_/¯

    IMO, even if you don't take your apps to the edge, it doesn't hurt to save even more memory so you can use that extra savings to improve your app. Let's say you have a policy of always leaving 2 KB free (or 10% free, or whatever). You can still save even more memory and do whatever you want with the savings, not to state the obvious.

    Many of these tips will make your code hard to read and difficult to maintain. IMO they apply more to datafields then a resource-heavy watch face which has a ton of available memory. All of this is from memory, so I apologize in advance if there are any errors.

    •  Don't use layouts. I have datafield apps where I created my own layout engine with fixed binary data hardcoded into arrays of bit-packed integers. This allowed the apps to run on devices with limited memory, and implement features which wouldn't have been possible if I had used layouts. Bit-packing can really help you out if you have a lot of data that can fit in less than 32 bits. e.g. If you have an array of 100 numbers between 0 and 255, you can save about 300 bytes by packing them into an array of 25 32-bit integers. IMO, 300 bytes is huge for datafields.
    • Don't use dictionaries. If practical, use if...else blocks instead of dictionary lookups. Even better, use array lookups instead of if...else blocks, when possible. (IIRC, switch statements are also relatively expensive.)
    •  Avoid using symbols where possible (including enums). As you have discovered, enums consume more memory at run-time compared to numerical constants
    • Keep the names of app properties short
    • Reduce the number of app settings (properties) Reduce the number of app resources (e.g. strings). If you load even one resource within the app, all of the resource tables will be loaded into RAM (which consumes memory based on the *number* of resources, not their size. The size of a resource only comes into play if you actually load it). Unfortunately, this includes settings strings. 
    • Avoid the use of Doubles and Longs where possible
    • Avoid the use of classes where possible.
      • e.g. If you need to pass / return structured data to / from a function, consider using an array instead of a class
      • e.g. If you have to implement an algorithm / data structure which lends itself to OOP/classes, just write it as a normal classless function with a pseudo-instance variable that gets returned/passed in.
    • In the same vein as above, a class instance variable uses more memory than a global variable. Global variables are usually evil, but if you really want to save every last byte....
    • Avoid using exceptions (because you have to create a class for that). For one app I implemented fatal (misconfiguration) error handling using a global var instead of an exception
    • If I'm not mistaken, there's also overhead with using barrels. If you have a shared library for your apps, instead of using barrels, consider just including it as a subfolder in Eclipse using a link
    • Try to write as little code as possible. Don't assume the compiler will optimize what you've written.
      • e.g. Use constants instead of variables where possible
      • Optimize if..else statements and other app logic by hand, as much as possible
      • If a function is a one-liner, "manually inline it" by removing the function definition and copying the body everywhere it's called
      • If repeated code is several lines, make it into a function

    Of course there are many cases when you wouldn't or can't follow the above (terrible) advice, like when you use the CIQ API and an argument requires the use of symbols. But you can design your own code to be as bare-bones as possible if you really want to save every byte. To mitigate problems with readibility and maintainability, you can comment your code heavily. I use constants instead of enums, but I have "enum names" in comments next to the constants. Not really the same thing, but better than nothing.

    Sometimes it's really just a tradeoff between saving memory and making your app user-friendly and/or making the code nice.

    For example, I have a datafield which has a "theme color" setting. For watches with more memory, I implement this as a nice drop-down in the app settings. Unfortunately this adds several strings to the resource table which sadly wastes memory at run-time.So for watches with less memory, I changed the theme color dropdown to a free-text string field which accepts a CSS color code, and I added a help link which documents the available codes to use. Pretty horrible UX, but in that case I prioritized features over UX. (The Connect IQ app settings have a lot of UX issues in any case.)

    (It would be nice if string resources for app settings were in a separate place, since they're almost never needed on the watch, but I guess that's neither here nor there.)

  • Avoid the use of Doubles and Longs where possible

    This gets back to "what is an object".  in monkey c, there are simple objects and complex ones.

    Simple objects are things like boolean, number and floats, where the value can be right in the object structure, while others, the object is a reference to the memory where the data is stored.  Doubles and longs would be an example of complex, where they are 64 bit values vs 32 for a float or a number. 

    There are also things like multi dimensional arrays.   If you define a 50x2 array, what you have is an array that's 50 in length, where each entry is an array that's 2 in length.  (an array is not a simple object).  So for the basic array it's 1+50 objects,

    If you use 2 arrays vs a 2 dimensional array, you use 2 objects - one for each array, vs 51.  Next, if this is used to store something like lat/lon, you can save a bunch by using 2 arrays with floats vs a two dimensional array with doubles.  On older devices, this can be a life saver where the number of objects is small..  I have one app that runs on any device, where the only real difference based on memory is how many lat/lon points it saves which I determine at runtime.  Just a case where a small change to how data is stored can make a huge difference.

    But wait, there's more!  Instead of 2 arrays, use one that's double the size needed, and store both lat lon there.  Something like every other value is one or the other, or the first half is one type, the second half the other.  You're down to 1 object for the basic structure.  

    Another thing to note with arrays, is with large ones, add() can be an issue.  When you add to an array, what's really going on is that new memory is allocated for the larger array, the old array is coped to it, then the old array is freed.  So for a bit, that array takes double the memory.  So there are cases where having the max size  and then keeping an index to what's used will work out better than growing an array.  Imagine an array that's 3k in a 16k CIQ DF and the impact of doing an add().

  • Keep the names of app properties short

    Very interesting and helpful tipp - makes a really noticeable difference.

    All other tipps are very informative as well - did do some of them already.

  • Thanks. Avoiding dictionaries and even nested arrays did bring down my watchfaces memory usage even more.