2019 – present (in development since 2017)
Baby steps was born1 after my friend and colleague Mathieu Badimon had twins. As he and his wife juggled full-time jobs with childcare, they struggled to keep track of all the relevant info about their young children. He went back and forth between various applications and paper logs, but nothing fit the bill. Baby Steps was created to solve this problem — to support co-parenting, to make entering information frictionless, and to present that info as simply and clearly as possible.
I was the only developer on the project, and handled all development responsibilities. The front-end and back-end are both written entirely in Swift, with the back-end using the Vapor framework.
All entries are stored in the app’s local SQLite database, and are synced to the cloud server and to other caretakers in the background. This means that past logs can be accessed quickly, and new logs added immediately, even without a network connection, so that busy parents don’t have to wait for network loads or troubleshoot spotty connections.
The center of the app’s interface is the feed, a familiar messaging metaphor used to display all entries for a child. This is built around a custom UICollectionView layout with infinite scrolling (past info is automatically loaded as you scroll back). The list can be filtered, and items can be long-pressed to edit, delete, or see more info. The list automatically configures itself to display users for children with multiple caretakers, and simplifies itself to hide user info if there is only one caretaker.
This section of the app visualizes the daily activity of a child in a way that allows patterns to emerge. The display is zoom-able, using a custom UIScrollView that zooms only on the y axis. Scrolling through time is virtualized, with data loading as needed.
A child’s height, weight, and head circumference can be visualized over time, and displayed against WHO reference curves for a child of that age.
This view uses a custom UIScrollView, where the chart’s lines remain sharp, and of constant width during zooming and zoom bouncing.
The charts section of the app lets you visualize entered data, and is custom coded from scratch.
To get a feel for how the app works before creating an account, the app offers a preview mode, where nearly all features are available, for a child with an automatically-generated history of logs. This history means no section of the app appears empty, and all the app’s visualizations can be appreciated.
This preview data is also used to show example data in visualization sections that would be empty, for stress-testing the app with large numbers of logs during development, for generating screenshots with realistic data, and for the fake baby app discussed below.
To get a feel for what it was like to use the app with a newborn baby2, I created a secondary app that would simulate the schedule of sleep, feeding, etc of a newborn baby, and notify me when I should log an event.
This app used the data generation code from the app’s preview mode to send local notifications, which would prompt me to manually log the event.
This turned out to be a great way to evaluate the app, and revealed many areas for improvement. It also let us to expand our group of beta testers to users who wanted to help, but didn’t have children of the right age to log.
One of our priorities during development was to make inputting data as seamless as possible. To facilitate that, we created a custom input tray that would slide up from the bottom of the device, and a number of custom input components (sliders, buttons, timers) to make it as easy and touch-friendly as possible to input those types of data.
As part of our mission to make inputting logs as easy as possible, We developed a custom time picker to enter single times or durations.
Input types can be re-organized to make the most frequently used items easily accessible, and to hide less-frequently-used items, in an interaction that resembles the springboard from iOS.
The sync server for the app is written in Vapor using server-side Swift. This means some code can be shared between the app and the server, for automatic serialization and deserialization of request and response objects using Codable, and for functions that are performed both client-side and server-side. Ubuntu Linux, NginX, and MySQL round out the back-end stack.