WSL2でExpo + E2Eテスト(MaestroとDetox)を試みて完全に詰んだ話

TL;DR WSL2環境でExpo(React Native)のE2EテストをMaestroとDetoxで試みたが、どちらもWSL2とWindowsエミュレータの構造的な問題で動かなかった。 かなり過言ではあるが、あえて感情的になるならば、Mobile開発においてMac以外は人権がない。というかあまりにもMac環境以外がだるすぎる。 環境 OS: Windows + WSL2(Ubuntu) Expo SDK 54 / React Native 0.81.5 New Architecture有効 Androidエミュレータ: Windows側で動作(Medium Phone API 36) ADB: Windows側のものをWSL2から参照 Maestroを試みる インストール curl -Ls "https://get.maestro.mobile.dev" | bash export PATH="$HOME/.maestro/bin:$PATH" ここで最初の罠。maestro --helpを叩くとAI系の全く別のCLIツールが応答した。同名の別アプリが先にPATHに入っていたため。$HOME/.maestro/binをPATHの先頭に置くことで解決。 フローの準備 # .maestro/add_and_complete_task.yml appId: com.example.myapp --- - launchApp - tapOn: text: "追加" - inputText: "テストタスク" - tapOn: text: "追加する" - assertVisible: text: "NOW" 実行して即死 You have 0 devices connected, which is not enough to run 1 shards. エミュレータはWindows側で動いており、adb devicesにはemulator-5554が見えている。しかしMaestroはWSL2側でデバイスを探すため認識できない。 --udid=emulator-5554を指定しても: Device emulator-5554 was requested, but it is not connected. maestro start-device --platform=androidを試みると: ...

February 23, 2026 · 2 min

react-native-pdf 6.7.7のiOS表示問題をpatch-packageで解決する

はじめに 業務でreact-native-pdfを使用した際、AndroidではPDFが正常に表示されるのにiOSでは表示されないという問題に遭遇しました。 この記事では、GitHubのissueで共有された解決策であるpatch-packageを使ったパッチ適用方法について解説します。 問題の概要 環境 { "react-native-pdf": "^6.7.7", "react-native": "0.80.1", "react-native-blob-util": "^0.22.2" } 症状 Android: PDF表示が正常に動作 iOS: PDFが表示されない この問題は、React Native 0.80以降でreact-native-pdfを使用した際に発生することが確認されています。 参考: pdf is not displayed,Android is working fine, but there are problems with iOS #966 解決策: patch-packageを使う GitHubのissueで@anhnguyen123さんが共有してくれたパッチファイルを適用することで、この問題を解決できます。 1. patch-packageのインストール まず、patch-packageとpostinstall-postinstallをdevDependenciesとしてインストールします。 # npmの場合 npm install --save-dev patch-package # yarnの場合 yarn add --dev patch-package postinstall-postinstall 参考: patch-package - npm 2. package.jsonにpostinstallスクリプトを追加 package.jsonのscriptsセクションに、postinstallスクリプトを追加します。 { "scripts": { "postinstall": "patch-package" } } このスクリプトにより、npm installまたはyarn installを実行するたびに、自動的にパッチが適用されます。 3. パッチファイルの配置 GitHubのissueからパッチファイルreact-native-pdf+6.7.7.patchをダウンロードし、プロジェクトルートにpatchesディレクトリを作成してそこに配置します。 ...

February 17, 2026 · 2 min

AsyncStorageって裏側何やってんの? - 2.0と3.0の実装の違いを調べてみた

私は普段React NativeでExpo触ってるので、AsyncStorageはよく使うんだけど、「そういえばAsyncStorageって裏側何やってんだろう?」って疑問が湧いてきたので調べてみることにした。 AsyncStorageの裏側 AsyncStorageのバージョンによって実装が少し違う。 AsyncStorage 2.0の実装 iOS/Androidのみ調査。 公式ドキュメント: Where your data is stored - Async Storage iOS (2.0) manifest.jsonファイルに保存される JSONファイル形式 パス: Documents/RCTAsyncLocalStorage_V1/manifest.json 詳細: 1024文字以下のデータはmanifest.jsonに、それより大きいデータは個別ファイル(MD5ハッシュ名)に保存される Android (2.0) SQLiteデータベースに保存される データベース名: RKStorage パス: /data/data/{package_name}/databases/RKStorage AsyncStorage 3.0 (next)の実装 公式ドキュメント: https://react-native-async-storage.github.io/3.0-next/ 対応プラットフォーム Android (SQLite) iOS (SQLite) ✨ macOS (SQLite) visionOS (legacy fallback, single database only) Web (IndexedDB backend) Windows (legacy fallback, single database only) iOS (3.0) SQLiteデータベースに変更された Androidと同じ実装に統一 パフォーマンスと安定性が向上 Android (3.0) 引き続きSQLite より洗練された実装 3.0からはiOSもAndroidも両方SQLiteになって、実装が統一されるそうだ。 互換性 React Native 0.76以降が必要(iOS/Android) Kotlin 2.1.0 iOS minimum target: 13 Android minimum SDK: 24 なぜiOSでmanifest.jsonからSQLiteに変更したのか あくまでも推測ではあるがやってみた。 ...

February 8, 2026 · 2 min

React NativeのTodoアプリで実装する相対時間ベースのプリセット機能

はじめに Todoアプリを使っていると、毎日・毎週繰り返す定型タスクの登録が面倒に感じることはありませんか? 「毎朝のルーチン」「週次ミーティングの準備タスク」など、同じタスクセットを何度も手入力するのは非効率です。この記事では、相対時間を使ったプリセット機能の実装方法を紹介します。 実装したアプリのソースコード: https://github.com/your-repo (適宜修正してください) 問題:絶対時間で期限を保存すると使い回せない 一般的なTodoアプリでプリセット機能を実装する場合、以下のような設計になりがちです: // ❌ よくある実装(絶対時間) interface PresetTask { text: string; dueDate: Date; // 2026-02-08 09:00:00 } この設計の問題点: プリセット作成時の日時が保存される 翌日読み込むと「昨日の9時」が期限になってしまう 毎回手動で期限を修正する必要がある 解決策:相対時間(dueHoursOffset)で管理する 代わりに、「今から何時間後」という相対的な時間で期限を管理します: // ✅ 相対時間ベースの設計 export interface PresetTask { id: string; text: string; priority?: Priority; dueHoursOffset?: number; // 現在時刻からの相対時間(時間単位) checklist?: string[]; } export interface Preset { id: string; name: string; tasks: PresetTask[]; createdAt: Date; } 公式ドキュメント: date-fns addHours: https://date-fns.org/v4.1.0/docs/addHours 実装の全体像 1. プリセット作成時の実装 プリセット編集画面では、期限を「現在時刻から何時間後」として入力します: // screens/PresetEditScreen.tsx const TaskInputRow = ({ item, index, onTaskTextChange, onDueHoursOffsetChange, // ... }: { item: PresetTask; index: number; onTaskTextChange: (index: number, text: string) => void; onDueHoursOffsetChange: (index: number, value: string) => void; // ... }) => { return ( <Card style={styles.taskCard}> <View style={styles.taskInputRow}> <TextInput label={`タスク ${index + 1}`} value={item.text} onChangeText={text => onTaskTextChange(index, text)} mode="outlined" style={styles.taskTextInput} autoComplete="off" autoCorrect={false} /> <TextInput label="期限(時間)" value={item.dueHoursOffset?.toString() || ''} onChangeText={value => { // 数字以外を除去 const filteredValue = value.replace(/[^0-9]/g, ''); onDueHoursOffsetChange(index, filteredValue); }} keyboardType="numeric" mode="outlined" style={styles.dueOffsetInput} /> </View> {/* ... */} </Card> ); }; ポイント: ...

February 8, 2026 · 4 min

三竦(さんすくみ)要件定義書

1. 概要 1.1 ゲームコンセプト 「三竦(さんすくみ)」は、犬・猿・雉の三すくみ関係を利用した追跡型対戦ゲーム。プレイヤーは召喚獣を配置して相手を攻撃しつつ、敵の召喚獣から逃げ切る戦略性とアクション性を兼ね備えたリアルタイムバトルゲーム。 1.2 コアメカニクス 三すくみ関係: 犬 → 猿 → 雉 → 犬 追跡システム: 召喚獣は相手プレイヤーを自動追跡 相性バトル: 有利な召喚獣は相手を一方的に倒す 戦略的配置: 召喚位置とタイミングが勝敗を分ける 1.3 開発目標 シンプル: ルールが3分で理解できる 完成優先: 1ヶ月以内にプレイアブル版完成 Android専用: Expo使用、まずCPU対戦のみ 2. ゲーム仕様 2.1 基本ルール 勝利条件 HP制: 各プレイヤーHP 3 制限時間: 3分 勝敗判定: 相手のHPを0にした方が勝ち 3分経過時、HP多い方が勝ち 同点の場合は引き分け ゲームフロー graph TD A[ゲーム開始] --> B[3分タイマー開始] B --> C{ゲーム中} C --> D[プレイヤー移動] C --> E[召喚獣配置] C --> F[召喚獣追跡] F --> G{当たり判定} G -->|当たった| H[HP-1] G -->|外れた| C H --> I{HP=0?} I -->|Yes| J[ゲーム終了] I -->|No| C C --> K{3分経過?} K -->|Yes| J K -->|No| C J --> L[リザルト表示] 2.2 召喚獣仕様 三すくみ関係 graph LR A[犬] -->|勝つ| B[猿] B -->|勝つ| C[雉] C -->|勝つ| A パラメータ表 召喚獣 速度 寿命 クールダウン 特性 犬 🐕 速い 10秒 10秒 素早く追跡、短命 猿 🐒 中速 10秒 10秒 バランス型 雉 🐦 遅い 10秒 10秒 遅いが長持ち 共通ルール: ...

February 6, 2026 · 6 min

Gemini CLIでExpo Todoアプリを爆速開発した話

やりたかったこと WSL2 環境で Expo を使った Todo アプリを作りたい。ただし、UI ライブラリの選定やナビゲーション設定など、細かい作業は Gemini に任せて効率化したい。 GEMINI.md でルール管理 プロジェクトルートに GEMINI.md を作成し、Gemini に従ってほしいルールを記載しました。 # Gemini AI Coding Rules ## Expo/React Native Specific - Use Expo SDK compatible packages only - Prefer `npx expo install` over `npm install` - Use functional components with hooks ## Expo Specific Rules - Use `@expo/vector-icons` instead of `react-native-vector-icons` - Never use packages that require native linking ## Tech Stack (Fixed) - Expo with TypeScript - React Native Paper for UI - AsyncStorage for persistence このファイルを事前に作っておくことで、Gemini が一貫した品質のコードを生成してくれます。 ...

January 31, 2026 · 2 min

React NativeでTextInputの日本語入力が壊れる問題と解決方法

最近趣味の方でモバイル開発を始めた。 Android端末を普段遣いしている点、仕事上iOSのアプリ周りのリリースがクソだるいことを知っているため Expoで開発しつつも、Androidのみを想定した開発を行っている。 その延長線で引っかかった部分とかをメモに残そうと思ったので記事にした。 問題:日本語入力で変換候補が消える Expo/React Native で Todo アプリを作っていたときTextInput で日本語を入力すると変換候補が一瞬で消えてしまう問題に遭遇。 // 問題のあるコード const [text, setText] = useState(''); <TextInput value={text} onChangeText={setText} /> 「あ」と入力しても変換候補が表示されず、即座に確定されてしまい、ローマ字入力も正常に動作しない。 原因:State更新による再レンダリング React Native の TextInput は制御コンポーネント(value + onChangeText)として使うと、以下の流れで問題が発生する。 日本語入力で「あ」と入力 OS が変換候補を表示するために内部バッファを保持 onChangeText が発火して State 更新 再レンダリングで TextInput が新しい value で再構築 controlled component としての value の強制が、IME の内部バッファと衝突する ← ここが問題! 変換候補が消える autoComplete や autoCorrect が有効だと、OS の補完機能が value の強制にさらに抵抗するため、IME との同期がズレやすくなる。 解決方法:autoComplete と autoCorrect を OFF // 修正後のコード <TextInput value={text} onChangeText={setText} autoComplete="off" autoCorrect={false} /> この2つのプロパティを追加するだけで、IME が安定して動作した。 ...

January 31, 2026 · 1 min