Connect IQ SDK

Application Types

ConnectIQ supports 4 different application types:


Watchfaces are a special application type that are run on the main clock screen of wearable devices. These application types are limited in a number of ways to allow them to have very minimal impact on the device’s battery life.

Watchface sleep

Watchfaces spend the majority of the time in “Sleep Mode” in this mode, execution is restricted to updates once each minute, and cannot use timers or animations. When a user raises the watch to look at it, the watchface exits sleep mode. When this occurs, the onExitSleep() method is called, and updates will increase to once per second, and timers and animations are allowed until the onEnterSleep() method is called.

module WatchUi
    //! The WatchFace class is used for creating watch faces
    //! that support exiting/entering low power mode.
    //! The developer will use the WatchFace View to tie in
    //! to gestures. This will enable the watchface to update
    //! at a faster rate than once a minute when the user has brought
    //! the watch "up", presumably to look at it. "Sleep"
    //! will be entered when the user looks away (lowers the watch)
    //! or the watch times out and automatically reenters sleep.
    //! @since 1.0.0
    class WatchFace extends Toybox.WatchUi.View
        //! onEnterSleep() is called when the watch is entering sleep mode.
        //! Terminate any active timers and prepare for slow updates.
        //! @since 1.0.0
        function onEnterSleep();

        //! onExitSleep() is called when the watch is exiting sleep mode.
        //! Timers and animations may be started here.
        //! @since 1.0.0
        function onExitSleep();

WatchFaceDelegate (2.3)

The WatchFaceDelegate provides input from the system to watchfaces. The delegate should be returned as the second element of the array returned from getInitialView() similar to input delegates for other application types. This delegate is currently only used to report power budget violations for watchfaces that support every second updates. If the execution budget is exceeded over the course of a minute, the onPowerBudgetExceeded() callback will be invoked, providing information about the execution time of the watchface, and the limit that was exceeded.

module WatchUi
    //! The WatchFace delegate is used to recieve events on a watchface
    //! @since 2.3.0
    class WatchFaceDelegate
        //! If the onPartialUpdate callback of the associated watchface view
        //! Exceeds power budget, onPowerBudgetExceeded will be called with
        //! information about the limits that were exceeded.
        //! @param [WatchUi.WatchFacePowerInfo] powerInfo The info object
        //! @since 2.3.0
        function onPowerBudgetExceeded(powerInfo);

    //! @since 2.3.0
    class WatchFacePowerInfo
        //! The average time onPartialUpdate took to complete.
        //! @return [Float] Average elapsed time per update in milliseconds
        //! @since 2.3.0
        var executionTimeAverage;
        //! The maximum time onPartialUpdate is allowed to take.
        //! @return [Float] Maximul allowed time in milliseconds
        //! @since 2.3.0
        var executionTimeLimit;

Every second watchface updates (2.3)

Some devices support updating the watchface every second. These devices will run normal updates via the onUpdate() method at the top of each minute like the other devices, and will additionally call the onPartialUpdate() method on all other seconds during the minute. The onPartialUpdate() method has very strict limits set on execution time, and must complete within these limits. If the execution limit is exceeded, the onPowerBudgetEceeded() method will be invoked in the WatchFaceDelegate, and partial updates will stop executing for the remainder of the app life-cycle.

When implementing onPartialUpdate() it is important to minimize the number of pixels updated on the display surface. Refreshing modified areas of the display is an expensive part of the update process. The VM will not automatically track which pixels have been modified as a part of a graphics call, so the application must use the Dc.setClip() method to restrict the rendering window when drawing to the display surface during an onPartialUpdate() callback. The VM considers all pixels in the active clip to be modified every time any pixel in the surface is modified.

Another useful tool for every second updates is using BufferedBitmap for complex graphics. If you have background graphics that must be redrawn each time watch hands, or other data is update in the foreground of your watchface, they can be rendered to a BufferedBitmap during the less restricted updates each minute, and copied as a single object to redraw background pixels during a partial update.

An example watchface that utilizes the every second watchface updates (Analog) can be found in the SDK samples.

module WatchUi
    class WatchFace extends Toybox.WatchUi.View
        //! onPartialUpdate() is called each second as long as the device
        //! power budget is not exceeded.
        //! It is important to update as small of a portion of the display as possible
        //! in this method to avoid exceeding the allowed power budget. To do this, the
        //! application must set the clipping region for the Graphics.Dc object using
        //! the setClip method. Calls to Toybox.System.println() and Toybox.System.print()
        //! will not execute on devices when this function is being invoked, but can be
        //! used in the device simulator.
        //! @param [Graphics.Dc] dc The drawing context
        //! @since 2.3.0
        function onPartialUpdate(dc);

Dynamic Data Fields

Dynamic Data Fields allow customers and third party developers to write data fields for Garmin activities. The goal is to make a system that not only makes it easy for a user to make a quick data field based off our workout data, but gives the developer the the power to customize the presentation.

Datafield and SimpleDataField

The base class for data fields is WatchUi.DataField. This class extends WatchUi.View, and in many ways behaves similarly to other WatchUi View objects. The onUpdate() method call will be made every time the watch needs to update.

In Garmin activities, the user controls the data page layout; specifically, whether it displays one, two, three, or more fields. The Connect IQ data field must handle displaying in all of those layouts, and the developer can use the simulator to test their field in all layouts supported by devices.

Many developers will only want to display a single value and not want to handle all the complexity of the drawing of a data field. In those instances, they can use a SimpleDataField object. A simple data field handles the drawing of the field in multiple sizes, and only requires the developer to implement a compute() method. The compute() method is passed an Activity.Info object, which contains all current workout information.

The following is an example of a “Beers Earned” data field, which displays how many beers you have “earned” during your workout:

using Toybox.Application;
using Toybox.WatchUi;

class BeerView extends WatchUi.SimpleField
    function initialize() {
        units = "beers";

    function compute(info) {
        return info.calories / 150; // Calories in average bottle of beer

class BeersEarned extends Application.AppBase
    function getInitialView() {
        return new BeerView();

Activity Info

The compute() method will be called before update in a data field. This method allows the developer to run a computation based on the workout information. The workout information is passed in as a Toybox.Activity.Info object. This object contains all the info about the workout that is computed by the system:

module Activity
    class Info
        var startLocation;              // Starting location of the activity, a Location object
        var startTime;                  // Starting time of activity, a Moment object
        var elapsedTime;                // Elapsed time of the activity in ms
        var timerTime;                  // Timer time in milliseconds
        var elapsedDistance;            // Distance in meters
        var currentLocationAccuracy;    // GPS Accuracy - See location constant
        var currentLocation;            // Current location, a Location object
        var calories;                   // Calories in kcal
        var currentSpeed;               // Speed in meters per second
        var averageSpeed;               // Average speed in meters per second
        var maxSpeed;                   // Maximum speed in meters per second
        var currentPace;                // Current pace in meters per second
        var currentPower;               // Current power in watts
        var averagePower;               // Average power in watts
        var maxPower;                   // Maximum power in watts
        var totalAscent;                // Total ascent in meters
        var totalDescent;               // Total descent in meters
        var currentHeartRate;           // Current heart rate in beats per minute
        var averageHeartRate;           // Average heart rate in beats per minute
        var maxHeartRate;               // Maximum heart rate in beats per minute
        var currentCadence;             // Current cadence in revolutions per minute
        var averageCadence;             // Average cadence in revolutions per minute
        var maximumCadence;             // Maximum cadence in revolutions per minute
        var swimStrokeType;             // Swim stroke type
        var swimSwolf;                  // Swim SWOLF
        var averageDistance;            // Swim stroke average distance
        var currentHeading;             // Current heading in radians
        // ...

Read more about compute() in the API documentation to gain a better understanding of it’s uses and possible limitations.

The following is the API for the data fields:

// The DataField class is used for creating data fields. In a DataField, the
// developer will implement a compute method, which is called by the system once
// per second, with an Activity.Info object. The system will call the onUpdate()
// method inherited from View when the field is displayed by the system.
class DataField extends View
    // To retrieve Activity.Info data for DataFields, it is necessary to override compute().
    // This is the method that is called to update the field information.
    // @param [Activity.Info] info The updated Activity.Info object
    function compute( info );

// The SimpleDataField class is used for creating simple data fields.
// In a SimpleDataField, the developer is only required to implement a compute
// method. The compute method is passed an Activity.Info object, which contains
// all current workout information. The compute method should return a value to
// be displayed.  Allowed types are Number, Float, Long, Double, and String.
// The SimpleDataField also contains a variable "label".  This variable should
// be assigned to a String label for the field.
class SimpleDataField extends DataField
    //! Constructor
    function initialize();

    //! To retrieve Activity.Info data for DataFields, it is necessary to override compute().
    //! This is the method that is called to update the field information.
    //! @param [Activity.Info] info The updated Activity.Info object
    function compute( info );

Simulating a Workout

To test your data field in the simulator, feed your data field simulated databy clicking the Simulation menu, choose FIT Data and then Simulate. This will generate random but valid data. You can also use Simulation > FIT Data > Playback File… to simulate a workout by using a pre-recorded FIT file.