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.
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
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.
(the lib/fitbit-weather
isn’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:
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 insteaddocument.getElementById(...)
anddocument.getElementsByClassName(...)
)- Use
htmlElement.style.fill
to change an element color instead ofhtmlElement.style.color
On the other part, you can use some of ES6 features like deconstruction const { prop } = obj
which is useful.
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:
You’ve to use the fill
property to change color.
Resources tips:
- For activities I used Fitbit icons
- For weather icons I used Flaticons
Interactions
Following the design, there’re interactions. I wanted the user to be able to interact with the clock face in a simple maner:
- 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.
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
In the code, this is how I added interactivity to the switch mode button:
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.
In the JavaScript I retrieved the DOM element with document.getElementById(...)
then added the onclick
event. In the event handler, I updated the tapeMode
variable, changed the image and saved the new tapMode
value 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:
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:
Then dispatch the data with the messaging
API (still in the companion) if you want to inform the app (on device):
Finally, here is how to intercept this value on the device:
Clock face app
In the clock app, you have to manually save and load settings by writing to a file with the fs
module:
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:
If you want to directly fetch a remote url you can use fetch
method:
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
:
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.
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.
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:
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.