StreakFire — Tuần 1–4
Build ứng dụng theo dõi thói quen iOS công khai: 4 tuần từ ý tưởng đến TestFlight, CloudKit sync và 12 người dùng đầu tiên.
Bối cảnh
Bối cảnh
Habit tracker là một trong những danh mục đông đúc nhất trên App Store. Vậy tại sao lại build thêm một cái nữa?
Vấn đề tôi gặp với mọi app habit tracking là như nhau: lỡ một ngày là streak về không, và cùng với đó là toàn bộ động lực. Những app này đang reward sự hoàn hảo thay vì sự kiên trì — đó là sai lầm thiết kế cơ bản.
StreakFire đặt cược vào một quy tắc khác. Streak phải sống sót được qua một lần lỡ bình thường của con người. Ẩn dụ “lửa” không phải ngẫu nhiên: lửa có thể tắt tạm thời nếu thiếu nhiên liệu, nhưng chưa chết hẳn. Một grace day — không reset bạn về số 0 — thay đổi toàn bộ hợp đồng cảm xúc giữa người dùng và app.
Tôi cũng muốn build cái này hoàn toàn công khai. Không stealth mode, không waitlist ẩn. Mọi quyết định kỹ thuật, mỗi build TestFlight, từng con số retention đều được post ngay khi có. Accountability công khai chính là cơ chế vận hành của dự án này.
Build log
Timeline 4 tuần ở trên là các cột mốc chính. Điều timeline không ghi lại là những ngõ cụt: một Core Data migration bị loại bỏ từ ngày thứ 3 để chuyển sang CloudKit, một widget design đẹp trên Figma nhưng tệ trên Lock Screen thật, và một notification permission flow viết lại hai lần trước khi cảm giác đúng.
Build log thật sự nằm trong nhật ký tuần. Phần này là tóm tắt — những quyết định còn giữ lại.
Lựa chọn công nghệ & đánh đổi
SwiftUI thay vì UIKit. Model khai báo của SwiftUI khớp tự nhiên với habit tracker: mô tả UI trông như thế nào tại bất kỳ streak count nào, SwiftUI lo phần transition. Đánh đổi là tính có thể đoán trước — animation SwiftUI đôi khi bất ngờ theo cách UIKit không bao giờ làm. Chấp nhận chi phí đó để đổi lấy tốc độ iteration.
CloudKit private database thay vì backend tự xây. Không server cần maintain, không auth cần build, Apple xử lý conflict resolution khi sync. Nhược điểm: không có server-side analytics, không query được dữ liệu toàn user. Với một sản phẩm solo build v1, đó là đánh đổi đúng. Backend có thể thêm sau nếu user base cần.
WidgetKit từ tuần đầu. Thêm widget support sau khi data model đã lock rất đau — cần thiết kế model với WidgetKit timeline stateless trong đầu từ trước. Làm ở tuần 2, trước khi model cố định, tiết kiệm một refactor lớn.
Không SDK analytics bên thứ ba. Privacy là ràng buộc thực tế trong danh mục habit tracker — người dùng nghi ngờ bạn đang track gì. Tôi build event logger tối giản ghi JSON ẩn danh vào CloudKit. Tôi có retention curve và funnel data; người dùng không có data nào rời khỏi hạ tầng Apple.
Điều tôi sẽ làm khác: tôi đánh giá thấp độ phức tạp của WidgetKit timeline scheduling trên các OS version cũ. iOS 16 và 17 xử lý widget refresh budget khác nhau. Mất một ngày cho vấn đề đó. Lần sau tôi sẽ viết WidgetKit layer thành test harness độc lập trước khi tích hợp vào app chính.
Số liệu — 30 ngày
12 người dùng TestFlight tính đến tuần 4. Con số nhỏ, và tôi không tô vẽ thêm. Đây đều là người tôi quen hoặc phản hồi qua bài build-in-public — không phải cold discovery. Điều con số này nói với tôi: core loop hoạt động — mọi người mở app mỗi ngày và log thói quen mà không cần tôi nhắc nhở.
- Crash-free rate: 99,8% qua 847 session (TestFlight Organizer)
- Day-3 retention: 58% cho cohort tuần 4 (4 trong 7 tester tuần 4 quay lại ngày thứ 3)
- Check-in trung bình mỗi user hoạt động: 1,4 thói quen mỗi ngày
- Thời gian session phổ biến nhất: dưới 30 giây — đúng như kỳ vọng với một check-in app
Con số retention đáng khích lệ với một v1 chưa có cơ chế giữ user nào ngoài push notification chưa được tối ưu. Đó là trọng tâm của tuần 5.
Nhìn lại
Bốn tuần vào đây, StreakFire là app thật được dùng bởi người thật. Đó là cột mốc duy nhất quan trọng ở giai đoạn này.
Những thứ đi đúng hướng: quyết định dùng CloudKit, đầu tư WidgetKit sớm, và quyết định ship lên TestFlight trong tuần 3 thay vì polish thêm hai tuần. Feedback từ 4 tester đầu tiên đã reshape hoàn toàn onboarding flow theo những cách tôi không thể dự đoán khi test một mình.
Những thứ chưa ổn: tôi mất quá nhiều thời gian vào visual design ngọn lửa trước khi có bất kỳ validation nào từ người dùng rằng ẩn dụ đó có resonance. Đây là lỗi điển hình — yêu thương brand trước khi chứng minh core mechanic. Mechanic hoạt động; visual ngọn lửa vẫn đang được iteration.
Bước tiếp theo: tuần 5 mang đến notification cá nhân hoá (để user tự đặt khung giờ nhắc), share card để post streak lên mạng xã hội, và lần đầu tiên submit lên App Store Connect. Mục tiêu là có listing public trên App Store trước cuối tháng Tư.
Tôi build cái này để ship, không phải để prototype. Record công khai là cơ chế accountability.
Build Log
Tuần 1
Nghiên cứu & xác định phạm vi
Khảo sát thị trường habit tracker hiện tại. Nhận ra vấn đề cốt lõi: hầu hết app tối ưu streak như chỉ số phù phiếm thay vì hỗ trợ người dùng phục hồi sau khi lỡ ngày. Xác định StreakFire chỉ cần hai tính năng nền — check-in hàng ngày và hiệu ứng 'lửa' sống sót sau một ngày bỏ lỡ.
Tuần 2
Data model + Widget PoC
Chọn CloudKit private database để đồng bộ không cần auth tự xây. Build struct HabitRecord và WidgetKit timeline entry đầu tiên. Xác nhận widget refresh đúng trong simulator mà không gặp lỗi entitlement.
Tuần 3
Tính toán streak + TestFlight đầu tiên
Implement engine tính streak bao gồm logic grace-day. Viết 14 unit test cho edge case (timezone, DST). Submit build 1 lên TestFlight; onboard 4 tester từ network cá nhân.
Tuần 4
Onboarding + đo lường retention
Thêm onboarding 3 màn hình kèm permission prompt cho notification. Tích hợp analytics ẩn danh qua event logger tự viết — không SDK bên thứ ba, privacy trước tiên. Day-3 retention cohort tuần 4: 58%.
Lựa chọn công nghệ & đánh đổi
Số liệu — 30 ngày
30-day snapshot
12
Active Users
Người dùng TestFlight seed, tuần 4