Tobi After Shipaton
Tobi didn't place at Shipaton, but I still shipped breaker panel sharing and a way to credit whoever did the work.
Tobi didn’t place at RevenueCat’s Shipaton. I’m not surprised. Growth was still thin, and I didn’t have time to market it while we were buying and moving into our own home and getting a new puppy.
I still shipped Tobi 1.6.0 in October. You can print or share a breaker panel as a PDF, share it as an image, and credit any household member on a task.
Numbers
Last month:
- MRR: $80
- iHog New Users: 67
- Camp Notes New Users: 22
- Tobi New Users: 153
This month:
- MRR: $87
- iHog New Users: 58
- Camp Notes New Users: 15
- Tobi New Users: 73
MRR is slowly increasing, but I’m a LONG way away from being able to pay my bills from my apps. I’m disappointed that my new users are falling so hard, but I’ll start pushing out content for Tobi and Camp Notes soon which should help get those back up.
Experiment Report
Last month, I reported on Tobi’s small September test of the hard paywall against freemium. One person in the freemium group set up a household, and nobody in the hard paywall group did. That wasn’t enough to call a winner, but I rolled freemium out to everyone.
People were still leaving at the name-your-home screen, which I had planned to remove. This November, I shipped a prefilled home name and a new view instead. I’ll watch whether people complete the home-naming step before deciding whether to remove it. If people still drop off, I might remove it altogether.
Technical Spotlight
In JABA, I save the time zone with every date something happened. Otherwise, something at 11 PM on September 30 in Los Angeles shows up as October 1 when you open the app in Tokyo. Here’s what it looks like.
import Foundation
struct DatedEvent {
let occurredAt: Date
let occurredTimeZone: String
func occurredDay() -> (year: Int, month: Int, day: Int)? {
guard let zone = TimeZone(identifier: occurredTimeZone) else { return nil }
let date = Calendar(identifier: .gregorian)
.dateComponents(in: zone, from: occurredAt)
guard let year = date.year, let month = date.month, let day = date.day else {
return nil
}
return (year, month, day)
}
}
occurredAt is the exact moment it happened, and occurredTimeZone is where it happened. occurredDay() reads the calendar date in that saved zone instead of the device’s current one, so the date doesn’t change after you travel. If the saved zone isn’t valid, it returns nil instead of guessing.
This Month’s Focus
In November, I am focused on the following:
- Adding new features to Tobi to make it easier to keep track of records and your home items warranties
- Getting JABA working locally
I'm looking for my next thing, whether that's full-time, consulting, or a long-term contract. If you're building something where the details matter, hire me on Toptal.