開発ブログ

Flutter / iOS / Android アプリ開発の実装メモ、Claude Code 活用、ピアノ・QRリーダー・メモ帳アプリの開発記録を公開しています。実装で詰まったポイント、設計判断、リリース後の課題対応など、個人開発者ならではの「泥臭い知見」を中心にまとめています。

開発記録 ギリギリジャンパー

名前入力でキーボードが出るとゲーム画面が縮む — resizeToAvoidBottomInset を切って、被る分だけカードを逃がす

ガラケーゲームシミュレータで、ランキング登録の名前を入れようとするとゲーム画面が 412 から 262 に縮みました。原因は Scaffold の既定値。1 行で直したら、その 1 行が消したキーボード回避を純関数で補うことになりました。テストが症状を再現していなかった話も含む開発記録です。

開発記録 ピアノ

ANR 率が品質基準の 9 倍 — 犯人は runApp() の前で await していた AdMob 初期化だった

Play Console の ANR 率が 4.41%、Google のしきい値の約 9 倍でした。犯人は main() で AdMob 初期化を await して first frame を遅らせていた 1 行。「開始」と「完了待ち」を分ける AdsInitializer で直し、レビューで見つかった 3 つの穴を塞ぎ、公開後はバージョン別 ANR 率 0.65% で効果を確かめるまでの記録です。

開発記録 メモ帳

「空になったら物理削除」がゴミ箱を素通り — メモ帳のサイレントデータロス 2 件をレビューで捕まえるまで

iOS 版メモ帳にゴミ箱を付けました。テストは全部通っていたのに、「本文を空にして保存」「写真メモの最後の 1 枚を削除」の 2 経路がゴミ箱を素通りして物理削除まで走っていました。レビューで P1 として出た経緯と、空メモ保存を新規・既存・ゴミ箱内の 3 分岐に整理した修正の記録です。

開発記録 万華鏡

回転する万華鏡でカメラ映像が伸び縮みする — AABB をやめて外接円で作る回転不変の描画矩形

万華鏡アプリのカメラ背景が、三角形の回転に合わせてびよんびよん伸び縮みしました。原因は回転する三角形の AABB へ画像を引き伸ばしていたことです。外接円に外接する正方形という回転不変な矩形と、BoxFit.cover 相当の中央クロップで解決し、純粋関数化と回帰テストで固定するまでの記録です。

学び 業務システム

平年の1月31日に1ヶ月足すと3月3日 — JavaScript Date の月加算オーバーフローと月末日クランプ

カレンダーUIの月送りを setMonth で実装したら、平年の1月31日から「翌月」を押しただけで3月3日に飛びました。2月がまるごと消えます。Date のフィールド正規化の仕組み、new Date(y, m+1, 0) による月末日クランプ、閏年まで含めた境界値テスト11件の記録です。

開発記録 ギリギリジャンパー

タイトル画面の黒帯が 1 tick で消える — ActionScript とタイムラインの優先順位セマンティクスを掘り当てるまで

自作の Flash シミュレータで、ガラケーゲームのタイトル画面の黒帯が一瞬で消えました。「白文字が不可視なだけ」という誤仮説に丸一日沈みます。毎秒スクショの証跡とレンダーツリーの逆引きで、ActionScript が変形したスプライトを後方 goto が巻き戻していたと突き止めるまでの開発記録です。

開発記録 プラットフォーム

Flutter デスクトップアプリが Cmd+Q で SIGSEGV — Timer と FFI とエンジン終了処理の競合を解く

Cmd+Q で閉じるたびに、macOS のクラッシュレポートが溜まっていました。PC=0 の SIGSEGV です。Timer.periodic の同期処理が sqlite3 の FFI を呼んでいる最中に、エンジンの終了処理がネイティブライブラリを解放していました。完了を待てる stop() と AppLifecycleListener.onExitRequested の二段構えで直すまでの記録です。

開発記録 ギリギリジャンパー

Flash Lite のガラケーゲームを Flutter で復刻する — SWF パーサーから自作してブラウザ公開するまで

ガラケー向けに作った自作ゲーム 6 本が、Flash の終了で遊べないまま何年も眠っていました。SWF4 パーサーも ActionScript VM も描画も Flutter で自作して、公式サイトの /play/ で復刻しました。フォーマット解析から公開までの記録です。20 年前のゲームがブラウザで動きました。

意思決定 ピアノ

34言語×2ストアのメタデータ運用を、正本1セット+fastlane でストアごと1コマンド化した話

ピアノアプリ(広告なし版)は34言語で App Store と Google Play に出しています。掲載文言の管理対象は68通り。手作業のコピペはとっくに破綻していました。1言語1ファイルの正本と Ruby パーサ、生成時の文字数上限検証、fastlane のレーン分離で、ストアごと1コマンドに集約した記録です。

開発記録 スキャナー

一度読んだコードがセッション中は二度と出ない——重複排除の範囲を「セッション全体」から「いま見えているカード」に縮めた話

複数のコードをまとめて読み取るスキャンアプリで、一度検出したカードが消えたあと、同じセッション中は二度と出てこない。かざしても無反応でした。重複排除を「セッション全履歴」から「いま画面に見えているカード」だけに縮めた設計変更と、履歴保存の非同期レース対策の記録です。

開発記録 万華鏡

万華鏡の描画を「毎タイル描き直し」から「1枚焼いて貼る」に変えた話

万華鏡アプリの描画が、敷き詰めたタイルの数だけ中身を描き直していました。三角形の中身を一度だけ画像に焼いて、各コピーに貼る方式へ変更。コストがタイル数×オブジェクト数の掛け算から足し算に落ちました。副産物で奥行き感まで付いた記録です。

開発記録 業務システム

同時承認で先着定員が壊れる — PostgreSQL の advisory lock で承認処理を直列化する

承認すると残高が増えて、先着の定員もある申請機能です。別々の申請を同時に承認したら、定員を超えて両方通りました。pg_advisory_xact_lock で直列化して、ロックを取ってから数え直す。実DBに同時リクエストを投げる e2e を書くまでの記録です。

学び プラットフォーム

通知のボタンを押すと即クラッシュ、ログは無言 — 犯人は 2 つ目の FlutterEngine だった

通知にアクションボタンを追加したら、iOS でどのボタンを押しても即クラッシュ。しかもファイルログには 1 行も痕跡が残りません。犯人は flutter_local_notifications がバックグラウンドで起動する 2 つ目の FlutterEngine と、そこへのプラグイン登録コールバックの見落としでした。

開発記録 メモ帳

10 年もののタブ並び替えライブラリを 3 回叩いて撤退 — メモ帳を UIKit Drag & Drop に乗り換えた話

iOS 版メモ帳のタブ並び替えがちらつく。10 年使ってきた BVReorderTableView を同じ日に 3 回パッチしても直らず、結局ライブラリを捨てました。UIKit 標準の UITableViewDragDelegate / UITableViewDropDelegate へ乗り換えるまでの、撤退判断と実装の勘所です。

意思決定 AI開発

AI レビューの指摘は 1 件 = 1 コミット — 「初回コミット → レビュー → 個別修正」のコミット粒度設計

AI レビューの指摘を 3 件まとめて 1 コミットで直していた頃、半年後の自分が git log を読んで何も分からなくなりました。「初回実装コミット → レビュー → 指摘ごとに個別コミット」に固定した理由 4 つと、amend を禁止している理由を、実際のコミット列と一緒に書きます。

開発記録 ピアノ

AdMob の落とし穴 4 つを一度に塞ぐ — 在庫切れ・オフライン・ライフサイクル・GDPR

ピアノアプリの AdMob まわりを見直したら、問題が 4 つ同時に出てきました。ロード完了を待てていない、失敗をユーザーに伝えていない、バックグラウンドでもリクエストが飛ぶ、GDPR 同意の位置が揃っていない。Completer と WidgetsBindingObserver と connectivity_plus で一度に整えた記録です。

開発記録 アルゴリズム

オフラインアプリに「世界ランキング」を実装する — Box-Muller 法と決定論的シード

サーバーを持たない完全オフラインのアプリで「上位 12%」を出したい。無茶だと思いました。平均 100・標準偏差 15 の正規分布に従う 10 万人の擬似母集団を Box-Muller 法で決定論的に生成し、統計モデルとしてのランキング体験を作った話です。

開発記録 位置情報

バッテリー最適化がデータの正しさを壊した話 — 22時間でFK違反1,427回

常時位置情報のアプリでバッテリーを削ろうとして、DB の外部キー違反が無限リトライする構造を自分で作っていました。実機ログは「22 時間で 1,427 回失敗・バッチ成功率 24%」。攻めた最適化が 2 連続で Revert され、地味な施策だけが生き残るまでの記録です。

開発記録 オーディオ

try/catch では捕まえられないクラッシュ — OSS オーディオプラグインをフォークして根治するまで

楽器系アプリで SIGSEGV のネイティブクラッシュが多発しました。犯人は OSS プラグインが iOS の構成変更通知の中で「親切に」オーディオエンジンを再起動していたことです。フォークを決めて、直列化キューと 500ms の隔離期間と panic CC で根治するまでの記録です。

ツール活用 AI開発

「AI が実装し、別の AI がレビューする」— 個人開発の二重 AI 体制を1年運用して

実装は Claude Code、レビューは Codex。この二重 AI 体制で個人開発を回しています。新作アプリが約 1.5 時間・18 コミットで MVP まで行った 1 セッション完結型と、5.5 週間・72 コミットのフェーズ駆動型。2 つの型と、それを支えるドキュメント設計の話です。

開発記録 位置情報

「電車に乗っていないのに電車判定」— GPS ジッターと軌跡の直線性で戦う

自宅にいるだけなのに「電車で移動中」と記録される。GPS ジッター起因の誤判定に、軌跡の直線性(直線距離÷経路距離)で散らばりを検出する対策を入れました。大きな修正を即日 Revert された話も含めた、センサーフュージョンの実録です。

開発記録 位置情報

OS のマイナーアップデートでバックグラウンド処理が静かに死んだ話

iOS のマイナーアップデート後、アプリ終了後のバックグラウンド位置記録が静かに止まっていました。プラグインの抽象を捨ててネイティブの Significant Location Change と CLVisit に降り、「アプリが死んでいる間に届くイベント」をバッファする設計で復旧するまでの記録です。

開発記録 アルゴリズム

7問でスコアが収束する軽量アイテム反応理論 — IRT をスマホローカルで動かす

1 日 7 問・約 5 分で、意味のあるスコアが出せるのか。不安なまま、項目反応理論(IRT)の Rasch モデルを簡略化しました。正誤と回答速度から能力値 θ を逐次更新する軽量スコアリングを、スマホローカルに実装した話です。学習率 0.6 はシミュレーションで決めました。

開発記録 公式サイト

Flutter Web を静的HTML化して SEO に対応した話 — インデックス5ページが39ページになるまで

Search Console で検査したら、クローラに見えていたのはほぼ空の HTML でした。Flutter Web の CanvasKit 描画のせいです。ビルド後プリレンダで当時の全42ルート(現在は46ルート)の静的HTMLを生成し、インデックス登録が5ページから39ページに増えるまでの実録です。

開発記録 ピアノ

評価10万件・400万DL・ドラマ出演 — ピアノアプリ開発で起きたうれしかった出来事

ピアノアプリのストア評価数が 10 万件を超えました。ダウンロード数も 400 万目前です。解読できない星 5 レビュー、テレビドラマ「ボーダレス」への登場。2013 年から個人開発を続けてきた中で、うれしかった出来事を時系列で振り返ります。

開発記録 ピアノ

ピアノアプリを二段鍵盤に対応させた話 — 同時押下の参照カウントとスクロール永続化

「鍵盤を 2 段にしてほしい」。以前からいただいていた要望に、ピアノアプリ v5.5.0 で応えました。高さの等分割、同じ鍵盤を上下から同時に押せる問題の参照カウント解決、段ごとのスクロール位置永続化、切り替え時の音鳴りっぱなし対策。実装で踏んだ穴まで含めて記録します。

ツール活用 Claude Code

Claude Code × MCP サーバーで外部ツール連携

Backlog を開いて、課題をコピペして、ストア管理画面へ。この往復がしんどかったです。MCP は Anthropic 標準のツール連携プロトコル。Backlog・App Store Connect・Playwright を Claude Code につなぐと、バグ報告から修正・ストア更新までが 1 つの会話で終わります。認証情報の渡し方と権限の絞り方もまとめました。

ツール活用 Claude Code

スラッシュコマンドとスキルで定型作業を自動化

定型作業をスキルにしておくと、あとはワンコマンドで終わります。TDD サイクル、ASO のメタデータ生成、14 言語のローカライズ。手で 14 ファイルを開いていたころに比べて、リリース 1 回あたり 1〜2 時間は浮きました。自作スキルの中身と、スキルにする基準の話です。

ツール活用 Claude Code

サブエージェントと Task で並列開発を回す

メモ帳アプリにタグ機能を足すとき、UI 実装・テスト作成・ドキュメント生成を 4 つのサブエージェントに分けて並列で走らせてみました。逐次なら 4 時間想定の作業が 1.5 時間。向く作業と向かない作業がはっきり分かれたので、その線引きと親エージェントの役割をまとめます。

開発記録 ギリギリジャンパー

ギリギリオンライン — タップゲームをオンライン対戦化する MVP

「激ムズ!! ギリギリジャンパー」をオンライン対戦にしたい。そう思って最初に作ったのは、オフラインの CPU 対戦版でした。通信レイヤーはゼロ。ゲームロジックを純粋関数に切り出して、対戦の体験だけを先に固めます。MVP の切り方と、そこからオンライン化へ向かう道筋の記録です。

ツール活用 Claude Code

CLAUDE.md の書き方で AI の働きが変わる

プロジェクトルートに置く CLAUDE.md は、Claude Code に対するもう一つのソースコードです。禁止事項を 2 つ書いてから、金額の丸め誤差もデータ消失も止まりました。書き方次第で事故が減り、提案の質も変わります。実体験ベースの構成例をまとめます。

ツール活用 Claude Code

Claude Code を個人開発のメインツールにしてみた

Cursor も GitHub Copilot も触ってきました。それが今、個人開発のメインツールは Claude Code に落ち着いています。決め手はターミナル完結でした。最初の 1 ヶ月で、コードを書く時間は 6 割減って、レビューの時間が 2 割増えています。何が変わったかの記録です。

開発記録 メモ帳

メモ帳アプリに Markdown プレビューを後付け実装中 — WKWebView の選択

純正 Swift のメモアプリに Markdown プレビューを作っています。ネイティブレンダラを書くか、WKWebView に任せるか。工数を数えて WKWebView にしました。エスケープでバグを踏み、スクロール位置でも詰まった進行中の記録です。次回アップデートで出します。

開発記録 メモ帳

メモ帳アプリに iCloud 同期を入れる — 全言語ローカライズの罠

「iPhone で書いたメモを iPad でも見たい」。いちばん多かったリクエストです。iCloud 同期そのものより、3 状態のインジケータと、全 16 言語の文言・キーワード更新のほうがしんどかったです。写真削除のクラッシュも一緒に踏みました。

開発記録 万歩計

万歩計アプリで歩行ルートに Kalman + RTS smoother を入れた話

1 秒ごとに取った GPS を地図に描いたら、まっすぐ歩いた道がジグザグでした。自分こんなに千鳥足だったかな、と。リアルタイムは Kalman フィルタ、後から見る軌跡は RTS smoother。地味な信号処理で自然な歩行ルートに整えるまでの話です。

開発記録 ここメモ

ここメモ — iOS バックグラウンドジオフェンスを Native Region Monitoring 化

「スーパーの近くで牛乳を思い出したい」。ここメモはそれだけのアプリです。でも Flutter の位置情報プラグインは、バックグラウンドでぜんぜん粘ってくれませんでした。iOS ネイティブの Region Monitoring に乗り換えて、権限フローまで組み直した記録です。

開発記録 QRリーダー

iPhone 13 mini 固有のカメラフォーカス問題と向き合った話

「iPhone 13 mini だけ QR コードが読み取れない」という低評価レビューが続きました。手元の検証機ではぜんぜん再現しません。原因は本体のカメラのフォーカス挙動で、CocoaPods の post-install フックでライブラリにパッチを当てて直しました。冪等性で自分の罠にはまった話も書きます。

開発記録 QRリーダー

ATT ダイアログをいつ出すか問題 — 起動直後の 1 秒遅延

ATT ダイアログを「いつ出すか」で何度もつまずきました。起動直後に出すと画面に乗らない。出さないとリジェクト。落ち着いたのは 1 秒の遅延と addPostFrameCallback の組み合わせでした。文言を変えたら許可率も上がった話も書きます。

UX メモ帳

メモ内のリンクをタップで開くか、ロングプレスで開くか

メモアプリのリンクは、タップで開くと編集できません。ロングプレスで開くようにしたら、今度は開き方に気づいてもらえませんでした。レビューで両方向の不満を受けて、表示モードと編集モードを分けるところに落ち着くまでの記録です。

デザイン 万華鏡

万華鏡アプリの BGM を「コード」じゃなく「モチーフ」で作り直した話

万華鏡アプリのヒーリング BGM を自分で流しっぱなしにしたら、5 分で飽きました。3 つのコードを回すのをやめて、4 つのモチーフをランダムに遷移させる作りに変えたら、眺めていられる時間が延びた話です。

開発記録 QRリーダー

「読み取った URL のタイトルを自動取得」機能を実装し、Revert した話

QR で読み取った URL のページタイトルを自動で取ってきて、履歴に表示する機能を作りました。約 30 行で動きました。それでも ATT とプライバシーの観点で考え直して、Revert しました。便利さより責務の範囲を選んだ話です。

開発記録 万華鏡

万華鏡アプリでテッセレーション沼にハマった話

「画面いっぱいに敷き詰めるだけ」のはずが、四隅に隙間ができました。RDP アルゴリズムの epsilon は 2.0 でガクガク、0.5 で重くなる。万華鏡アプリのテッセレーション(敷き詰め)まわりで、何度もやらかした記録です。

開発記録 メロメモ

メロメモ — 鼻歌を音階に変換するピッチ検出エンジンを TDD で書いた

鼻歌で録音した音声を、楽譜風の音階データに変換するアプリを作りました。ピッチ検出エンジンは「A4 のサイン波を入れたら 69 が返る」という 1 本のテストから始めています。約 90 のテストを GREEN に保ったまま、Phase ごとに積み上げた記録です。

開発記録 ふりがなメモ

ふりがなメモ — メモアプリにふりがな機能を統合する

メモ帳にふりがなを振るだけのつもりが、フォルダ・単語グループ・設定と機能が増えていきました。共有プロバイダーと DAO を先に作り、機能をひとつずつ独立に動かしながら MVP 85% まで持ってきたアプリの構成です。

開発記録 ピアノ

サスティンペダルを CC#64 イベントとして録音再生する仕組み

自分の録音を聴き返して、ぶつ切りなことに気づきました。ペダルが入っていないからです。サスティン(CC#64)を録音タイムラインに載せるため、DB スキーマを v6 にして CcEvents テーブルを追加。ON と OFF のエッジだけ記録し、停止時の同期でつまずいた記録です。

開発記録 N消し

N消し — OpenCV でナンバープレートを認識してマスクする

駐車場の写真に車が 10 台。ナンバープレートを手でぼかしていたら日が暮れます。OpenCV の形状検出でプレートを自動で見つけて、複数枚まとめてマスクするアプリを作りました。検出パイプラインとマルチプレート対応の設計をまとめます。

開発記録 ピアノ

ピアノアプリにベロシティ(強弱)を実装するまで

もう少し強弱を付けたい、というレビューの要望から、鍵盤のベロシティ対応を始めました。0〜127 の MIDI 値を返す純関数にして、pressure 非対応の端末は Y 座標に逃がす。stylus と通常タッチの取り違えで緊急修正も出しました。14 言語ぶんの設定 UI までの記録です。

開発記録 ピアノ

AUSamplerクラッシュとの戦い — オーディオ再初期化を整理する

Crashlytics を入れたら、AUSampler のクラッシュが一気に見えるようになりました。電話着信や Bluetooth 切替のたびに再初期化が連鎖していたのが原因です。旧サウンドフォントのアンロード、再初期化回数の上限、トリガを中断イベントだけに絞る。この三段構えで落ち着くまでの記録です。

開発記録 MultiTimer

MultiTimer — Clean Architecture + Drift で複数タイマーを実装

煮込み 15 分と焼き 5 分を同時に走らせたいのに、標準のタイマーは 1 つしか動きません。そこを解決したくて作ったのが MultiTimer です。Clean Architecture で Domain / Data / Presentation の 3 層に分け、下から積み上げた手順を共有します。

開発記録 モンスターブレイカー

モンスターブレイカー — ブロック崩しと RPG を融合させた Flame ゲーム

ブロック崩しは単調、RPG は操作が複雑。だったら混ぜてみよう、で始めたのがモンスターブレイカーです。Flame でコアロジックを純 Dart に切り出し、Wave 制バトルとパーティ・装備・呪文を載せました。初期 HP 5 だとすぐ死ぬ、みたいな調整の記録です。

開発記録 ピアノ

電話着信後にピアノが鳴らなくなる問題をネイティブ実装で解決

「電話に出た後にアプリに戻ると音が鳴らなくなる」。ピアノアプリのレビューに定期的に届く報告でした。プラグインの設定では直らず、最終的に iOS ネイティブで中断通知を購読して、割り込み終了後に setActive(true) を呼び直すところで決着します。Method Channel で Dart 側につないだ記録です。

デザイン ピアノ

黒鍵盤に光沢を入れたら、レビューが少しだけ優しくなった

久しぶりに自分のアプリを普通に触って、黒鍵盤が安っぽいことに気づきました。やったのは斜めの線形グラデーションを重ねただけ。3 色のストップで光沢の山を作ると、急に本物っぽくなります。コードは 30 行いかない改修なのに、反応は想像以上でした。