Flutter / iOS / Android アプリ開発の実装メモ、Claude Code 活用、ピアノ・QRリーダー・メモ帳アプリの開発記録を公開しています。実装で詰まったポイント、設計判断、リリース後の課題対応など、個人開発者ならではの「泥臭い知見」を中心にまとめています。
ガラケーゲームシミュレータで、ランキング登録の名前を入れようとするとゲーム画面が 412 から 262 に縮みました。原因は Scaffold の既定値。1 行で直したら、その 1 行が消したキーボード回避を純関数で補うことになりました。テストが症状を再現していなかった話も含む開発記録です。
Play Console の ANR 率が 4.41%、Google のしきい値の約 9 倍でした。犯人は main() で AdMob 初期化を await して first frame を遅らせていた 1 行。「開始」と「完了待ち」を分ける AdsInitializer で直し、レビューで見つかった 3 つの穴を塞ぎ、公開後はバージョン別 ANR 率 0.65% で効果を確かめるまでの記録です。
iOS 版メモ帳にゴミ箱を付けました。テストは全部通っていたのに、「本文を空にして保存」「写真メモの最後の 1 枚を削除」の 2 経路がゴミ箱を素通りして物理削除まで走っていました。レビューで P1 として出た経緯と、空メモ保存を新規・既存・ゴミ箱内の 3 分岐に整理した修正の記録です。
万華鏡アプリのカメラ背景が、三角形の回転に合わせてびよんびよん伸び縮みしました。原因は回転する三角形の AABB へ画像を引き伸ばしていたことです。外接円に外接する正方形という回転不変な矩形と、BoxFit.cover 相当の中央クロップで解決し、純粋関数化と回帰テストで固定するまでの記録です。
カレンダーUIの月送りを setMonth で実装したら、平年の1月31日から「翌月」を押しただけで3月3日に飛びました。2月がまるごと消えます。Date のフィールド正規化の仕組み、new Date(y, m+1, 0) による月末日クランプ、閏年まで含めた境界値テスト11件の記録です。
自作の Flash シミュレータで、ガラケーゲームのタイトル画面の黒帯が一瞬で消えました。「白文字が不可視なだけ」という誤仮説に丸一日沈みます。毎秒スクショの証跡とレンダーツリーの逆引きで、ActionScript が変形したスプライトを後方 goto が巻き戻していたと突き止めるまでの開発記録です。
Cmd+Q で閉じるたびに、macOS のクラッシュレポートが溜まっていました。PC=0 の SIGSEGV です。Timer.periodic の同期処理が sqlite3 の FFI を呼んでいる最中に、エンジンの終了処理がネイティブライブラリを解放していました。完了を待てる stop() と AppLifecycleListener.onExitRequested の二段構えで直すまでの記録です。
ガラケー向けに作った自作ゲーム 6 本が、Flash の終了で遊べないまま何年も眠っていました。SWF4 パーサーも ActionScript VM も描画も Flutter で自作して、公式サイトの /play/ で復刻しました。フォーマット解析から公開までの記録です。20 年前のゲームがブラウザで動きました。
ピアノアプリ(広告なし版)は34言語で App Store と Google Play に出しています。掲載文言の管理対象は68通り。手作業のコピペはとっくに破綻していました。1言語1ファイルの正本と Ruby パーサ、生成時の文字数上限検証、fastlane のレーン分離で、ストアごと1コマンドに集約した記録です。
複数のコードをまとめて読み取るスキャンアプリで、一度検出したカードが消えたあと、同じセッション中は二度と出てこない。かざしても無反応でした。重複排除を「セッション全履歴」から「いま画面に見えているカード」だけに縮めた設計変更と、履歴保存の非同期レース対策の記録です。
万華鏡アプリの描画が、敷き詰めたタイルの数だけ中身を描き直していました。三角形の中身を一度だけ画像に焼いて、各コピーに貼る方式へ変更。コストがタイル数×オブジェクト数の掛け算から足し算に落ちました。副産物で奥行き感まで付いた記録です。
承認すると残高が増えて、先着の定員もある申請機能です。別々の申請を同時に承認したら、定員を超えて両方通りました。pg_advisory_xact_lock で直列化して、ロックを取ってから数え直す。実DBに同時リクエストを投げる e2e を書くまでの記録です。
通知にアクションボタンを追加したら、iOS でどのボタンを押しても即クラッシュ。しかもファイルログには 1 行も痕跡が残りません。犯人は flutter_local_notifications がバックグラウンドで起動する 2 つ目の FlutterEngine と、そこへのプラグイン登録コールバックの見落としでした。
iOS 版メモ帳のタブ並び替えがちらつく。10 年使ってきた BVReorderTableView を同じ日に 3 回パッチしても直らず、結局ライブラリを捨てました。UIKit 標準の UITableViewDragDelegate / UITableViewDropDelegate へ乗り換えるまでの、撤退判断と実装の勘所です。
AI レビューの指摘を 3 件まとめて 1 コミットで直していた頃、半年後の自分が git log を読んで何も分からなくなりました。「初回実装コミット → レビュー → 指摘ごとに個別コミット」に固定した理由 4 つと、amend を禁止している理由を、実際のコミット列と一緒に書きます。
ピアノアプリの AdMob まわりを見直したら、問題が 4 つ同時に出てきました。ロード完了を待てていない、失敗をユーザーに伝えていない、バックグラウンドでもリクエストが飛ぶ、GDPR 同意の位置が揃っていない。Completer と WidgetsBindingObserver と connectivity_plus で一度に整えた記録です。
サーバーを持たない完全オフラインのアプリで「上位 12%」を出したい。無茶だと思いました。平均 100・標準偏差 15 の正規分布に従う 10 万人の擬似母集団を Box-Muller 法で決定論的に生成し、統計モデルとしてのランキング体験を作った話です。
常時位置情報のアプリでバッテリーを削ろうとして、DB の外部キー違反が無限リトライする構造を自分で作っていました。実機ログは「22 時間で 1,427 回失敗・バッチ成功率 24%」。攻めた最適化が 2 連続で Revert され、地味な施策だけが生き残るまでの記録です。
楽器系アプリで SIGSEGV のネイティブクラッシュが多発しました。犯人は OSS プラグインが iOS の構成変更通知の中で「親切に」オーディオエンジンを再起動していたことです。フォークを決めて、直列化キューと 500ms の隔離期間と panic CC で根治するまでの記録です。
実装は Claude Code、レビューは Codex。この二重 AI 体制で個人開発を回しています。新作アプリが約 1.5 時間・18 コミットで MVP まで行った 1 セッション完結型と、5.5 週間・72 コミットのフェーズ駆動型。2 つの型と、それを支えるドキュメント設計の話です。
自宅にいるだけなのに「電車で移動中」と記録される。GPS ジッター起因の誤判定に、軌跡の直線性(直線距離÷経路距離)で散らばりを検出する対策を入れました。大きな修正を即日 Revert された話も含めた、センサーフュージョンの実録です。
iOS のマイナーアップデート後、アプリ終了後のバックグラウンド位置記録が静かに止まっていました。プラグインの抽象を捨ててネイティブの Significant Location Change と CLVisit に降り、「アプリが死んでいる間に届くイベント」をバッファする設計で復旧するまでの記録です。
1 日 7 問・約 5 分で、意味のあるスコアが出せるのか。不安なまま、項目反応理論(IRT)の Rasch モデルを簡略化しました。正誤と回答速度から能力値 θ を逐次更新する軽量スコアリングを、スマホローカルに実装した話です。学習率 0.6 はシミュレーションで決めました。
Search Console で検査したら、クローラに見えていたのはほぼ空の HTML でした。Flutter Web の CanvasKit 描画のせいです。ビルド後プリレンダで当時の全42ルート(現在は46ルート)の静的HTMLを生成し、インデックス登録が5ページから39ページに増えるまでの実録です。
ピアノアプリのストア評価数が 10 万件を超えました。ダウンロード数も 400 万目前です。解読できない星 5 レビュー、テレビドラマ「ボーダレス」への登場。2013 年から個人開発を続けてきた中で、うれしかった出来事を時系列で振り返ります。
「鍵盤を 2 段にしてほしい」。以前からいただいていた要望に、ピアノアプリ v5.5.0 で応えました。高さの等分割、同じ鍵盤を上下から同時に押せる問題の参照カウント解決、段ごとのスクロール位置永続化、切り替え時の音鳴りっぱなし対策。実装で踏んだ穴まで含めて記録します。
Backlog を開いて、課題をコピペして、ストア管理画面へ。この往復がしんどかったです。MCP は Anthropic 標準のツール連携プロトコル。Backlog・App Store Connect・Playwright を Claude Code につなぐと、バグ報告から修正・ストア更新までが 1 つの会話で終わります。認証情報の渡し方と権限の絞り方もまとめました。
定型作業をスキルにしておくと、あとはワンコマンドで終わります。TDD サイクル、ASO のメタデータ生成、14 言語のローカライズ。手で 14 ファイルを開いていたころに比べて、リリース 1 回あたり 1〜2 時間は浮きました。自作スキルの中身と、スキルにする基準の話です。
メモ帳アプリにタグ機能を足すとき、UI 実装・テスト作成・ドキュメント生成を 4 つのサブエージェントに分けて並列で走らせてみました。逐次なら 4 時間想定の作業が 1.5 時間。向く作業と向かない作業がはっきり分かれたので、その線引きと親エージェントの役割をまとめます。
「激ムズ!! ギリギリジャンパー」をオンライン対戦にしたい。そう思って最初に作ったのは、オフラインの CPU 対戦版でした。通信レイヤーはゼロ。ゲームロジックを純粋関数に切り出して、対戦の体験だけを先に固めます。MVP の切り方と、そこからオンライン化へ向かう道筋の記録です。
プロジェクトルートに置く CLAUDE.md は、Claude Code に対するもう一つのソースコードです。禁止事項を 2 つ書いてから、金額の丸め誤差もデータ消失も止まりました。書き方次第で事故が減り、提案の質も変わります。実体験ベースの構成例をまとめます。
Cursor も GitHub Copilot も触ってきました。それが今、個人開発のメインツールは Claude Code に落ち着いています。決め手はターミナル完結でした。最初の 1 ヶ月で、コードを書く時間は 6 割減って、レビューの時間が 2 割増えています。何が変わったかの記録です。
純正 Swift のメモアプリに Markdown プレビューを作っています。ネイティブレンダラを書くか、WKWebView に任せるか。工数を数えて WKWebView にしました。エスケープでバグを踏み、スクロール位置でも詰まった進行中の記録です。次回アップデートで出します。
「iPhone で書いたメモを iPad でも見たい」。いちばん多かったリクエストです。iCloud 同期そのものより、3 状態のインジケータと、全 16 言語の文言・キーワード更新のほうがしんどかったです。写真削除のクラッシュも一緒に踏みました。
1 秒ごとに取った GPS を地図に描いたら、まっすぐ歩いた道がジグザグでした。自分こんなに千鳥足だったかな、と。リアルタイムは Kalman フィルタ、後から見る軌跡は RTS smoother。地味な信号処理で自然な歩行ルートに整えるまでの話です。
「スーパーの近くで牛乳を思い出したい」。ここメモはそれだけのアプリです。でも Flutter の位置情報プラグインは、バックグラウンドでぜんぜん粘ってくれませんでした。iOS ネイティブの Region Monitoring に乗り換えて、権限フローまで組み直した記録です。
「iPhone 13 mini だけ QR コードが読み取れない」という低評価レビューが続きました。手元の検証機ではぜんぜん再現しません。原因は本体のカメラのフォーカス挙動で、CocoaPods の post-install フックでライブラリにパッチを当てて直しました。冪等性で自分の罠にはまった話も書きます。
ATT ダイアログを「いつ出すか」で何度もつまずきました。起動直後に出すと画面に乗らない。出さないとリジェクト。落ち着いたのは 1 秒の遅延と addPostFrameCallback の組み合わせでした。文言を変えたら許可率も上がった話も書きます。
メモアプリのリンクは、タップで開くと編集できません。ロングプレスで開くようにしたら、今度は開き方に気づいてもらえませんでした。レビューで両方向の不満を受けて、表示モードと編集モードを分けるところに落ち着くまでの記録です。
万華鏡アプリのヒーリング BGM を自分で流しっぱなしにしたら、5 分で飽きました。3 つのコードを回すのをやめて、4 つのモチーフをランダムに遷移させる作りに変えたら、眺めていられる時間が延びた話です。
QR で読み取った URL のページタイトルを自動で取ってきて、履歴に表示する機能を作りました。約 30 行で動きました。それでも ATT とプライバシーの観点で考え直して、Revert しました。便利さより責務の範囲を選んだ話です。
「画面いっぱいに敷き詰めるだけ」のはずが、四隅に隙間ができました。RDP アルゴリズムの epsilon は 2.0 でガクガク、0.5 で重くなる。万華鏡アプリのテッセレーション(敷き詰め)まわりで、何度もやらかした記録です。
鼻歌で録音した音声を、楽譜風の音階データに変換するアプリを作りました。ピッチ検出エンジンは「A4 のサイン波を入れたら 69 が返る」という 1 本のテストから始めています。約 90 のテストを GREEN に保ったまま、Phase ごとに積み上げた記録です。
メモ帳にふりがなを振るだけのつもりが、フォルダ・単語グループ・設定と機能が増えていきました。共有プロバイダーと DAO を先に作り、機能をひとつずつ独立に動かしながら MVP 85% まで持ってきたアプリの構成です。
自分の録音を聴き返して、ぶつ切りなことに気づきました。ペダルが入っていないからです。サスティン(CC#64)を録音タイムラインに載せるため、DB スキーマを v6 にして CcEvents テーブルを追加。ON と OFF のエッジだけ記録し、停止時の同期でつまずいた記録です。
駐車場の写真に車が 10 台。ナンバープレートを手でぼかしていたら日が暮れます。OpenCV の形状検出でプレートを自動で見つけて、複数枚まとめてマスクするアプリを作りました。検出パイプラインとマルチプレート対応の設計をまとめます。
もう少し強弱を付けたい、というレビューの要望から、鍵盤のベロシティ対応を始めました。0〜127 の MIDI 値を返す純関数にして、pressure 非対応の端末は Y 座標に逃がす。stylus と通常タッチの取り違えで緊急修正も出しました。14 言語ぶんの設定 UI までの記録です。
Crashlytics を入れたら、AUSampler のクラッシュが一気に見えるようになりました。電話着信や Bluetooth 切替のたびに再初期化が連鎖していたのが原因です。旧サウンドフォントのアンロード、再初期化回数の上限、トリガを中断イベントだけに絞る。この三段構えで落ち着くまでの記録です。
煮込み 15 分と焼き 5 分を同時に走らせたいのに、標準のタイマーは 1 つしか動きません。そこを解決したくて作ったのが MultiTimer です。Clean Architecture で Domain / Data / Presentation の 3 層に分け、下から積み上げた手順を共有します。
ブロック崩しは単調、RPG は操作が複雑。だったら混ぜてみよう、で始めたのがモンスターブレイカーです。Flame でコアロジックを純 Dart に切り出し、Wave 制バトルとパーティ・装備・呪文を載せました。初期 HP 5 だとすぐ死ぬ、みたいな調整の記録です。
「電話に出た後にアプリに戻ると音が鳴らなくなる」。ピアノアプリのレビューに定期的に届く報告でした。プラグインの設定では直らず、最終的に iOS ネイティブで中断通知を購読して、割り込み終了後に setActive(true) を呼び直すところで決着します。Method Channel で Dart 側につないだ記録です。
久しぶりに自分のアプリを普通に触って、黒鍵盤が安っぽいことに気づきました。やったのは斜めの線形グラデーションを重ねただけ。3 色のストップで光沢の山を作ると、急に本物っぽくなります。コードは 30 行いかない改修なのに、反応は想像以上でした。