PN: 2483-AX9
DO NOT REMOVE DURING OPERATION
SN: 002194-C
MAT: AL6061-T6
信頼
Book a call
Client
Tapnote

Hardening the in-app lecture recorder for 100k students

Impact of
our work

プロジェクトについて
Project type
Mobile audio reliability engineering
Tech stack
React Native
Expo
Swift
Kotlin
Sentry
built in
Ongoing
15s flush
incremental on-device persistence
Storage parity
iOS matched Android's free-space threshold
Sentry coverage
native errors now reach the team

About
the project

プロジェクト
Tapnote is a study app trusted by over 100,000 students at universities including Harvard, Oxford, Cambridge, Stanford, MIT, and Yale. Students upload files or record audio in-app and Tapnote turns lectures into flashcards, mind maps, quizzes, and fill-in-the-blanks.
The hard part lives in the native module. A single lost recording is a lost lecture, and the lectures students rely on can be three hours long.

The
challenge

挑戦
ID.5.001
Tapnote's in-app lecture recorder was generating a steady stream of support tickets. Students were losing recordings, sometimes hours of class, to phone interruptions, low storage, the app being backgrounded, or the process dying. The underlying Swift + Kotlin native module held the entire recording in memory and only persisted when the user hit stop, so any one of those everyday events wiped the whole session. Every failure was a frustrated user reaching out to support, and the failures were nearly impossible to reproduce in dev without literally recording for an hour straight.

The
solution

解決策
ID.1.001
Chunked native recorder
15-second flush interval with segmented capture files and safe finalize, so a single interruption can never wipe more than the last segment.
ID.1.002
Additive native surface
The existing React Native recording hook and modal flow kept working unchanged. New behaviour is additive only, so the rest of the app shipped untouched.
ID.1.003
Sentry-first observability
Native errors from Swift and Kotlin route into Sentry with structured tags, dashboards, and breadcrumbs, so production failures stop being silent.
ID.1.004
iOS storage parity
Matched Android's 100 MB free-space threshold on iOS via the system filesystem APIs, surfaced through a single typed event the product can render however it wants.
ID.1.000
We rewrote the native recorder around incremental persistence and proved it under real load, so recordings now survive the edge cases that were generating the tickets. Audio flushes to disk every 15 seconds in segmented files instead of buffering the entire session in memory. A phone call, an app crash, or a dead battery now costs at most the last 15 seconds, not the whole lecture. The native module's public surface did not change, so the existing React Native recording hook, recording UI, and recovery flow kept working without rewrites.
We then closed the gaps that kept the recorder from being production-grade. Native errors from Swift and Kotlin now route into Sentry with structured tags and breadcrumbs, so the Tapnote team can spot the next failure mode before it becomes a wave of tickets. iOS got a free-space check at parity with Android's existing 100 MB threshold, exposed as a typed low-storage event, so students see a warning before they lose anything, instead of after.
The rework merged into Tapnote's production repo and shipped to users through the app's standard release.

Impact of
our work

プロジェクトについて
15s flush
incremental on-device persistence
Storage parity
iOS matched Android's free-space threshold
Sentry coverage
native errors now reach the team