こんにちは!日々、プログラミング学習や個人開発に励んでいる皆さん。
今回は、私がTypeScriptの基礎を学ぶ過程で開発し、最終的にAndroidアプリとしてリリースした独自のTodoアプリ「mono タスク管理」の開発ストーリーをお届けします。
「Webフロントエンドの技術(TypeScript)でどうやってAndroidアプリを作ったの?」 「localStorageを使ったタブごとのデータ保存で、どこにつまずいてどう解決したの?」
といった、開発者が直面しやすいリアルな悩みや工夫したポイントを、実際のコードを交えて詳しく解説します。これからTypeScriptを学びたい方や、個人アプリをリリースしてみたい方の参考になれば幸いです。
1. 「mono タスク管理」ってどんなアプリ?
まずは、今回リリースしたアプリの概要を簡単にご紹介します。
- アプリ名: mono – タスク管理
- プラットフォーム: Android
- 主な機能: Todoの追加、編集、削除(タスク管理の基本機能のみ)
- 操作性: 直感的で無駄のないシンプルなデザイン
世の中には多機能なタスク管理アプリが溢れていますが、「機能が多すぎて使いこなせない」「もっと直感的に、今やるべきことだけを整理したい」と感じたことはありませんか? 「mono todo」は、そんな引き算の美学から生まれた、「迷わない、迷わせない」究極にシンプルなTodoアプリです。
2. UI/UXのこだわり:タブ切り替えによるスマートなタスク管理
このアプリを開発する上で、最もこだわった(工夫した)ポイントが「タブ切り替えによるTodoの管理」です。
通常のTodoアプリは、1つの画面にすべてのタスクが羅列されるか、複雑なカテゴリ分け(フォルダ機能など)が必要になることが多いです。しかし「mono」では、画面上部のタブをタップするだけで、表示するTodoを瞬時に切り替えられるようにしました。
なぜ「タブ切り替え」なのか?
- 視覚的なノイズを減らす: 「仕事」「プライベート」「買い物」など、今見たいコンテキストのタスクだけを画面に表示できます。
- 直感的な操作感: スマホユーザーにとって馴染みのある「スワイプやタップによるタブ切り替え」を採用することで、説明書なしで誰でもすぐに使いこなせます。
機能は「追加・編集・削除」に絞り込み、操作は極めてシンプル。だからこそ、日常のあらゆるシーンに溶け込む操作性を実現しました。
3. 【最大の難所】localStorageへの保存とタブ切り替えの罠
開発自体はスムーズに進んだ部分もありましたが、最も苦労したのが「各タブごとのTodoデータをlocalStorage(ローカルストレージ)へ保存・取得する処理」でした。
Webアプリ(WebViewをベースにしたAndroidアプリ)において、手軽にデータを永続化できるlocalStorageは非常に便利です。今回は「タブごとにデータを独立して管理する」ために、以下のようなロジックを実装しました。
実装したストレージ処理のコード
実際のデータ管理には、動的にキーを生成してタブごとのデータを読み書きする、以下のような関数を作成しました。
/** localStorage のキーを一元管理 */
const storageKey = (tabId: string) => `todos_${tabId}`;
/** localStorage から ToDo[] を読み込む */
const loadFromStorage = (tabId: string): ToDo[] => {
try {
const json = localStorage.getItem(storageKey(tabId));
return json ? (JSON.parse(json) as ToDo[]) : [];
} catch {
return [];
}
};
/** ToDo[] を localStorage に保存する */
const saveToStorage = (tabId: string, todos: ToDo[]): void => {
localStorage.setItem(storageKey(tabId), JSON.stringify(todos));
};
直面した「上手く取得できない」問題点
この設計はロジックとして非常に明快なのですが、いざ「タブ切り替え」のUIと組み合わせると、データの取得が上手くいかない現象(バグ)に直面しました。
- タブ切り替え時のタイミングのズレ: タブを切り替えた瞬間、画面のコンポーネントが再レンダリングされる前に古いタブのデータが一瞬表示されたり、新しいタブの
tabIdの読み込みが間に合わず、データが空([])として判定されてしまう。 - ステート(State)の更新と同期の罠: アプリ内の変数(状態)が更新されるタイミングと、
saveToStorage/loadFromStorageが実行されるタイミングが非同期の中で噛み合わず、せっかく保存したはずのTodoが、切り替えの拍子に上書きされて消えてしまう。
どうやって解決したか?
この問題(上手く取得できない現象)を解決するために、「タブ切り替え(tabIdの変更)」を検知して、確実に最新のデータをlocalStorageから引き出すライフサイクル(または副作用の制御)を徹底しました。
具体的には、タブが切り替わったイベントをトリガーにして、まず古いタブのデータを確実に saveToStorage で保存し、その後に新しい tabId で loadFromStorage を実行してアプリ内の状態(State)を上書きする、という「シーケンス(順序)」を厳密に管理するようにしました。
この「一見シンプルに見える裏側で、データの整合性を保つ」という経験は、TypeScriptの型定義(ToDo[] のアサーションなど)や、Webアプリにおける状態管理の深い理解に繋がり、非常に良い勉強になりました。
4. TypeScriptを学んで、WebアプリからAndroidアプリへ
今回、なぜ数ある言語の中からTypeScriptを選び、最終的にAndroidアプリとしてリリースしたのか。そのロードマップをご紹介します。
Step 1: TypeScriptの基礎学習
JavaScriptの「型がないことによるバグ(データ構造の勘違いなど)」に悩まされていたため、静的型付けができるTypeScriptを学び始めました。今回のコードでも as ToDo[] と型を明示しているように、コンパイル時点でエラーを検知してくれる安心感は、個人開発において強力な味方になります。
Step 2: WebアプリとしてTodoアプリを構築
まずはブラウザ上で動くWebアプリとして「mono」の原型を作りました。ここで前述のlocalStorageのロジックや、タブ切り替えのUIを徹底的にテストしました。
Step 3: Androidアプリへの移植・リリース
Webアプリとして完成したコード(HTML/CSS/TypeScriptをビルドしたJavaScript)をベースに、ハイブリッドアプリ開発の技術を活用してAndroidアプリへと変換し、Google Playストアへのリリースを果たしました。
使い慣れたWeb技術(TypeScript)の知識だけで、スマートフォンで動くアプリをストアにリリースできる。このスピード感とワクワク感は、現代の開発環境ならではの大きなメリットだと実感しています。
あ、Android化はCapacitorを使いました。
5. まとめと今後の展望
「mono」は、機能としては「追加・編集・削除」しかない、非常に割り切ったアプリです。しかし、その中には「タブ切り替えによる快適なUI」と「localStorageの挙動と戦いながら実装したデータ管理の工夫」が詰め込まれています。
技術の勉強を始めたばかりの頃は、「こんなシンプルなアプリを作っても意味があるのかな?」と思ってしまうこともあります。しかし、実際に作って、バグに苦しみ、コードを修正してストアにリリースするまでのプロセスには、教科書を読むだけでは絶対に得られない「生きた知識」が溢れていました。
今後は、ユーザーの声を聴きながら、さらに直感的に使えるような細かなブラッシュアップを行っていきたいと考えています。
気になった方は、ぜひAndroid端末にインストールして、その「シンプルな心地よさ」を体感してみてください!
最後までお読みいただきありがとうございました! この記事が、TypeScriptを勉強中の方や、localStorageのデータ管理で悩んでいる方の参考になれば嬉しいです。


コメント