New app launched but basically no users
February 2026 recap and what I'll be doing in March.
New monthly series recapping my work to get to $1,000 MRR
At the end of January, I launched my latest app, Track, a simple habit tracker app. I have had a few new users since launch, but it's a struggle to get found in this market with how saturate it is. It seems that almost everyone is making YouTube videos about how to make habit tracker apps with AI and I'm sure that's not helping me. The good news is I launched this as an app I needed and not one that I really hoped would truly move the needle.
Numbers
I set a goal for myself last October to get to $1,000 MRR by October 2026. Here are some numbers I'm keeping track of to make sure I'm on track.
This Month:
MRR: $48
Track New Users: 61
iHog New Users: 21
Camp Notes New Users: 17
Last Month:
MRR: $43
Track New Users: 51
iHog New Users: 38
Camp Notes New Users: 28
Right now, I don't think I'm on track to hit my goal, but I am going to start pushing Camp Notes more as it's getting warmer in the United States.
Experiment Report
I launched Track to see if a new app would result in some increase in numbers, and it did increase new users, but not really move any needle for the revenue. I do believe this is due to lack of marketing and the fact that it's a habit tracker, which as I said at the beginning is an oversaturated market.
Technical spotlight
I make a specific ErrorService that logs the errors to the analytics platform so I can see what is happening. Here is what it looks like.
protocol IdentifiableError: Sendable {
var id: String { get }
}
enum ErrorLevel: String {
case warning
case critical
case fatal
}
struct ErrorService {
static func reportError(
error: Error,
level: ErrorLevel = .warning,
logger: Logger,
function: String = #function,
file: String = #file
) {
let errorId: String
if let identifiableError = error as? any IdentifiableError {
errorId = identifiableError.id
} else {
errorId = error.localizedDescription
}
let logInfo = """
🚨 Error At: \(file).\(function)
ID: \(errorId)
Message: \(error.localizedDescription)
"""
logger.error("\(logInfo, privacy: .public)")
AnalyticsManager.track(
event: "Error",
properties: [
"errorId": errorId,
"errorMessage": error.localizedDescription,
"function": function,
"level": level.rawValue
]
)
}
}
The IdentifiableError allows me to group errors together. ErrorLevel allows me to focus on which errors I need to investigate, warning I watch until a certain threshold, critical I try to get to almost immediately, and fatal always get taken care of as soon as I see them come in. The ErrorService is a struct so I can call it from anywhere and be able to send it in the same format to the analytics platform. If you notice I also store which function called the error, so it's easier to debug later.
This month's focus
In March, I am focusing on the following:
Marketing for Camp Notes
How to make Track more enticing for subscribers
Continue working on an envelope budgeting app, JABA.
My current idea is that the more apps I have on the App Store, the more chances I have to really have something take off. I know I also need to market though as I'm not getting many users which means my analytics aren't the most useful.



