Beginner guide overview: Develop a Fitbit clock face

rootasjey
10 min readNov 24, 2018

--

Ionic clock face animation

When I started coding my clock face, I wasn’t expecting the need to work several weeks on it. The first result and prototyping can be fast but handling specifics scenarios and multiple devices support add a good level of complexity.

In this overview, I’ll explain why my development was longer than I expected and how I faced different problems.

The associated project to this article is my metrix clock face available on GitHub.

Summary

  • First draft
  • Fitbit Studio vs. Local IDE
  • Simulator
  • Architecture
  • JavaScript
  • Design
  • Fonts
  • Images
  • Interactions
  • Settings
  • Fetch Web APIs
  • Handle permissions
  • Multi-devices
  • Common issues
  • Conclusion

First draft

I’ve a Fitbit Ionic since several months and I regularly switch clock face (some are really great). But after using almost all free clock faces, I thought there were missing designs (it always begins like this).

So I took the time to draft a quick clock that I wanted and went to Fitbitdev to start developing. I already had a clear idea of the design I wanted to create.

First draft

In my drawing, each line is a ‘metric’.

A metric is composed of:

  • A text value
  • An icon (facultative)

The first seps are quite easy with the official tutorial and with a template starter. I chose the Digital Clock one, and I got my first result super fast: you can build & deploy the clock face right away after using the template to create a new project.

Fitbit Studio vs. Local IDE

Fitbit Studio

When I saw that the dev team had created a Command Line Interface , I was super enthousiastic because I was going to code inside my familiar Text Editor/IDE (VS Code) with auto-completion and other tools to speed up my work. But I was disappointed because the CLI has not a watch mode yet, neither a single command to build & deploy. So I had to execute the following:

  • Write code in my Text Editor/IDE
  • Switch to the terminal
  • Execute fibit$ build to build the clock
  • Execute fitbit$ install to deploy the app to the simulator
  • Switch to the emulator to see the result

This process was fine at the beginning but became quickly tedious because developing an user interface demands you to be pixel perfect when the screen size is so small. So I had to repeat this flow process a lot of time to perfectly place my elements and this was too much.

I came back to Fitbit Studio where I only had to click once to build & deploy.

In my opinion, the work flow need real improvements in 2 ways:

Fitbit Studio

  • Add auto-completion
  • Different colors schemes
  • Multiple tabs (to have multiple openned files)
  • Linter
  • Code folding

An easy way to add all these features would be to use an existing online editor like CodeSandbox but maybe there’d be business conflicts.

CLI

  • One command to build & deploy to the emulator
  • Watch mode option to auto build & deploy on file save
  • Deploy to real devices (not sure it’s not possible but I couldn’t do it — feel free to comment if I’m wrong)

I think the CLI solution would be the better way to improve as the missing features with the Studio are erased with a local editor/IDE.

Simulator

This is a quick section to advise you to download and use the simulator for prototyping if you don’t already have it. It will drastically improve your workflow as it’s faster to deploy your app on it instead of your physical device.

When I first started with the SDK, the simulator did not exist and it was much slower to design a clock’s user interface.

This is one of the useful tools the Fitbit dev team added recently.

There’s sadly no Linux version at the moment. And there’re some missing features like permissions control (right now all deployed app has full access).

You can download the simulator from the Gettings Started page.

Architecture

When creating a new Fitbit project, you can select a template which creates a predefined files & folders structure for you, which is nice to begin with.

Predefined files & folders structure

(the lib/fitbit-weatherisn’t part of the default file structure)

At first, I writed all my additional code in the app/index.js file, but it became too verbose. As it easier for me to structure my logic with small modules, I separated my index.js code as follow:

Structure in app/
Structure in app/activities/

And inside index.js I call the necessary modules.

You can check the whole files structure here.

JavaScript

Just a note about the Fitbit’s JavaScript version which has a different subset of available features from the browser.

Encountered JS missing features (not exhaustive):

  • Object.assign doesn’t exist
  • Changing class of HTMLElement isn’t possible
  • Some of the Math functions are missing
  • document.querySelector(...) doesn’t exist
    (use instead document.getElementById(...) and document.getElementsByClassName(...) )
  • Use htmlElement.style.fill to change an element color instead of htmlElement.style.color

On the other part, you can use some of ES6 features like deconstruction const { prop } = objwhich is useful.

Design

Versa design

I learnt that when designing for a so small screen size, every pixel matters. You’ve to carefully choose where to place texts, images and in which order. Sometimes you can’t display all the information you want so you either have to crop or to remove the nun-releveant ones. Your screen has to stay clean and information has to go to the point. On a watch, users usually don’t spend a lot of time, it’s often a peek view. So if the screen is messy, it may not do a good job.

NOTE: It’s possible to have a lot of information displayed but it has to be distinguishable with colors or text style and format.

Fonts

Currently only bult-in fonts are supported. You can simule custom fonts by using images.

Images

I used icons provided by Fitbit design icons repository which are .png images of 48x48 pixels. If you intend to change the color of your images, make sure you can by trying to apply different colors in JavaScript or CSS code.

In CSS:

#icon { fill: red; }

In JavaScript:

Change icon’s Color

You’ve to use the fill property to change color.

Resources tips:

Interactions

Following the design, there’re interactions. I wanted the user to be able to interact with the clock face in a simple maner:

Interactions prototype
  • A tap on a text change its format
  • A tap on an icon switch to the next activity

It looked great and functional on paper and on simulator, but on my device this wasn’t working at all. The text and the icon were too close and it was hard to tap on the right element.

So I decided to only have one interaction on text + icon:

  • A tap on a metric switch to the next activity

But I wanted to keep the different formats possibility, so an activity could be displayed in 3 possible ways:

  • In total number (e.g. 42000)
  • Percentage (e.g. 150%)
  • Or value left to reach goal/value over the goal. (e.g. -30 or +30)

I decided to add a switch mode button at the bottom left of the screen.

Switch mode button at the bottom left

Now, when an user would be in switch to next activity mode, it’d be the same as before:

  • A tap on a metric switch to the next activity

And when the user would be in switch format mode:

  • A tap on a metric switch to the next format

Some of the recommandations I learnt while designing my clock face:

  • Space up different interaction areas
  • If two or more interactions areas are too close, some of them may not react on tap
  • Interactive images should at least be 48x48 pixels for the ease of contact
Interaction areas concept

In the code, this is how I added interactivity to the switch mode button:

Clock face’s tap mode switcher

In resources/index.gui, I put an <image/> inside a <g/> container. I added pointer-events="visible" property to make the image react to interactions.

Add clic/tap event on icon to react to interactions

In the JavaScript I retrieved the DOM element with document.getElementById(...) then added the onclick event. In the event handler, I updated the tapeModevariable, changed the image and saved the new tapModevalue to the settings.

Settings

If your clock face can be personalized or is interactive, you probably want to save user settings and load them later. If you allow different refresh time for weather update, for example.

You can enable the settings/personalization at two places:

Settings mobile app

In the Settings mobile app (on the phone) key/values are automatically persisted in the settings module with the settingsKey specified:

index.jsx settings app

I created a <Select/> element which can take multiple values (15, 30, …). When a value will be selected by the user, it will be persisted in the settingsStorage with the weatherRefreshTime key.

To retrieve the value, you can use the following code in your companion:

Retrieve settings data

Then dispatch the data with the messaging API (still in the companion) if you want to inform the app (on device):

Send message from companion to app

Finally, here is how to intercept this value on the device:

Intercept message sent from companion

Clock face app

In the clock app, you have to manually save and load settings by writing to a file with the fs module:

App’s settings save & load

You can keep an object let settings = {}; to keep your data in-memory then write this object to a file when quitting the clock face with fs.writeFileSync( ... ). You can then load the settings again with fs.readFileSync( ... ).

Your clock face will close when another app will open, for example going to the alarms, activities or settings apps. It’s a good moment to save your settings at that time. Your clock face will load back when returning on the main screen.

Fetch Web APIs

To fetch Web APIs like weather data, you’ll need to add a companion.

A companion is an extra JavaScript runtime to enhance a Fitbit app/clock face. In the following code it’s used to communicate with Web APIs.

I used the fitbit-weather module, so my companion code is very short and simple:

Fitbit-weather companion usage

If you want to directly fetch a remote url you can use fetch method:

fetch usage

After fetching data from the companion, you can send it to the app with the messaging API.

Handle permissions

At the beginning it was easier to suppose that the user would give all permissions to the clock face, but when I came close to publish, I knew that I had to handle possible permissions denial.

First you need to tick the needed permissions in your package.json :

Permissions in package.json

The selected permissions will be asked to the user when s.he will want to install your app.

You can now use the API to check for permissions with the appbit module.

In my app, each metric has is own logic, so I handle their permissions individually.

Handle permissions in Fitbit clock face

For example, the active minutes metric is allowed if the user has granted activity access. The weather metric needs location and internet access. And the clock and date can always be selected as they don’t need permission.

I added a logic to automatically switch to the next allowed activity if the current one has been denied. Thus, I can avoid showing a text error to the user.

You can see all available permission in the dev guide.

Multi-devices

This step was the last I worked on before releasing my clock face. As I only own a Fitbit Ionic, I first designed the user interface for its screen size.

Thanks to the simulator, I could test my app with a Versa’s screen size and I had to adjust some texts and icons positions to get a good result.

Ionic version
Versa version

To detect on which device your app is running, the doc advises you to test dimensions:

The if (!device.screen) part is for older SDK versions which don’t have device.screen property.

I created a layout module which places visual elements according to the device:

How to place visual elements according to device detection

Instead of using conditional clauses I used objects with specific keys. Now if I want to places horizontally my icon, I can simply use const x = getIconX() . The returned value will be 290 if the current device is an Ionic and 250 otherwise.

Similarly, the getIconY({ metricNumber }) returns the appropriate value depending on the passed value and the current deviceType . The returned value will be 30 if the current device is a Versa and we want to place the metric number 0 .

Common issues

While developing, I encountered some issues which were out of my hands and I found very few information online about these:

  • Sometimes Fitbit Studio won’t find any of my devices nor simulators and will show ‘Error loading’. I’ve tested on Chrome, Firefox, Safari, with the same result.
  • One time, my Fitbit Ionic would’t want to connect to the debugger displaying the message: ‘Connection error 1006’. I saw some people having the exact same issue on forums but no definitive solution was given. Well in my case, a simple restart of my device resolved the issue. But it took me some time to test different solutions as the error code wasn’t much of use.

Conclusion

Developing a Fitbit clock face was nice but with some frustration, so I thought I’d share my experience feedback for new comers.

Sources

--

--