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

Emacsのdotfilesをモジュール化してメンテナンス性を向上させた話

背景 約900行に肥大化したmypackage.elを整理し、機能ごとにファイル分割してメンテナンス性を向上させるリファクタリングを実施した。 課題 単一ファイルの肥大化: mypackage.elが900行超えで見通しが悪い 機密情報の混在: API keyがコード内に散在 使っていない設定: コメントアウトされた設定が残存 パッケージの把握困難: 何を使っているか不明瞭 新しいディレクトリ構成 dotfiles/emacs/ ├── init.el # エントリーポイント ├── early-init.el # 起動高速化 ├── core/ │ ├── env.el # 環境変数・基本設定 │ ├── custom.el # UI基本設定 │ ├── keymap.el # キーバインド │ └── util.el # ユーティリティ関数 ├── packages/ │ ├── manager.el # straight.el設定 │ ├── core.el # 基盤パッケージ │ ├── completion.el # 補完系 (Vertico, Corfu) │ ├── search.el # 検索系 (Consult, Embark) │ ├── git.el # Magit等 │ ├── lsp.el # Eglot等 │ ├── languages.el # 言語別設定 │ ├── ai.el # GPTel, Ollama │ ├── writing.el # Denote, Org, Markdown │ ├── ui.el # テーマ、アイコン │ └── optional.el # たまに使うもの ├── templates/ # Tempelテンプレート └── docs/ └── README.md 重要な学び: require vs load 問題: requireでパッケージが読み込まれない 当初、init.elで(require 'completion)のように読み込んでいたが、以下の問題が発生: ...

February 6, 2026 · 3 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

GEMINI.mdでAIに開発履歴を管理させる方法

問題:AIは過去の失敗を忘れる Gemini や ChatGPT などの AI にコード生成を依頼するとき、こんな問題がありませんか? 同じミスを何度も繰り返す 前回指摘したルールを忘れる プロジェクト固有の制約を無視する 毎回「Expo では react-native-vector-icons じゃなくて @expo/vector-icons を使って」と指示するのは面倒です。 解決策:GEMINI.md でルールを管理 プロジェクトルートに GEMINI.md ファイルを作成し、AI に従ってほしいルールをすべて記載します。 # Gemini AI Coding Rules ## General Principles - Always provide complete, working code - Include all necessary imports - Add TypeScript types for all functions ## Expo/React Native Specific - Use Expo SDK compatible packages only - Prefer `npx expo install` over `npm install` ## Expo Specific Rules - Use `@expo/vector-icons` instead of `react-native-vector-icons` - Import example: `import { MaterialCommunityIcons } from '@expo/vector-icons';` AI に指示を出すときは、必ず「GEMINI.md を読んでから実装して」と伝えます。 ...

January 31, 2026 · 3 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

Proxmox LXCコンテナでJupyterLab環境構築 - 試行錯誤とトラブルシューティング

はじめに Proxmox上にJupyterLabのLXC環境を構築しました。当初はGeminiに任せて試行錯誤しましたが、最終的にベストプラクティスに辿り着いたので、その過程と解決策をまとめます。 構築の基本方針 当初は「Root + グローバル環境」で構築しようとしましたが、最終的に**「専用ユーザー + 仮想環境(venv)」**による安全でクリーンな構成に落ち着きました。 最終構成 OS: Ubuntu 24.04 LTS (LXC Container) ユーザー: jupyter (非Root運用) Jupyter: JupyterLab (v4.x) 環境: /opt/jupyter/venv (OSと分離した仮想環境) 環境構築手順 1. OSの準備 Ubuntu 24.04の最小構成に必要なパッケージをインストールします。 apt update && apt upgrade -y apt install -y python3-full build-essential python3-fullが重要です。これがないと後述するPEP 668の問題に直面します。 2. 専用ユーザーとディレクトリの作成 # 専用ユーザー作成 useradd -m -s /bin/bash jupyter # Jupyter本体用のディレクトリ準備 mkdir -p /opt/jupyter chown jupyter:jupyter /opt/jupyter 3. 仮想環境の構築 jupyterユーザーとして、OSの制限を受けない独立した環境を作ります。 su - jupyter python3 -m venv /opt/jupyter/venv source /opt/jupyter/venv/bin/activate # JupyterLabとカーネルのインストール pip install jupyterlab ipykernel pandas 4. systemdによるデーモン化 /etc/systemd/system/jupyter.serviceを作成します。 ...

January 26, 2026 · 2 min

PythonとWorld Bankデータで世界の産業構造を可視化する方法

はじめに 世界の国々の経済は、どのように変化しているのでしょうか?今回は、World Bank(世界銀行)が提供するオープンデータを使って、各国の産業構造の変化を可視化してみました。 この記事では、Pythonのpandasとmatplotlibを使って、1997年から2024年までの約30年間の産業構造の変化をグラフにする方法を紹介します。 産業構造とは? 経済学では、産業を3つに分類します: 第一次産業:農業、林業、漁業など(自然から直接資源を得る産業) 第二次産業:製造業、建設業など(原材料を加工する産業) 第三次産業:サービス業、金融、小売など(形のないサービスを提供する産業) 国が経済発展すると、第一次産業から第二次産業へ、そして第三次産業へとシフトしていく傾向があります。これを「産業構造の高度化」と呼びます。 使用したデータ World Bankが提供している以下のデータを使用しました: 第一次産業: Agriculture, forestry, and fishing, value added (% of GDP) 第二次産業: Industry (including construction), value added (% of GDP) 第三次産業: Services, value added (% of GDP) これらは各産業がGDP(国内総生産)に占める割合を示しています。 分析対象国 今回は、経済発展段階や地域が異なる10カ国を選びました: 日本(JPN): 先進国・アジア 中国(CHN): 新興国・急成長 アメリカ(USA): 先進国・北米 ドイツ(DEU): 先進国・欧州 インド(IND): 新興国・南アジア 韓国(KOR): 先進国・アジア インドネシア(IDN): 新興国・東南アジア ベトナム(VNM): 新興国・急成長 シンガポール(SGP): 先進国・都市国家 タイ(THA): 新興国・東南アジア ポーランド(POL): 中所得国・欧州 Pythonコード 以下が実際に使用したコードです。 # 第一次: https://data.worldbank.org/indicator/NV.AGR.TOTL.ZS # 第二次: https://data.worldbank.org/indicator/NV.IND.TOTL.ZS # 第三次: https://data.worldbank.org/indicator/NV.SRV.TOTL.ZS # 産業付加価値GDP import pandas as pd import matplotlib.pyplot as plt # CSVデータ取得 df_1 = pd.read_csv('1.csv', skiprows=3) df_2 = pd.read_csv('2.csv', skiprows=3) df_3 = pd.read_csv('3.csv', skiprows=3) # 国名のマッピング country_names = { 'JPN': 'Japan', 'CHN': 'China', 'USA': 'United States', 'DEU': 'Germany', 'IND': 'India', 'KOR': 'South Korea', 'IDN': 'Indonesia', 'VNM': 'Vietnam', 'SGP': 'Singapore', 'THA': 'Thailand', 'POL': 'Poland', } def to_chart(df, codes, begin, end, title, filename): plt.figure(figsize=(14, 9)) for code in codes: data = df[df['Country Code'] == code] years = [str(year) for year in range(begin, end)] dict_data = data[years].iloc[0].to_dict() years = list(dict_data.keys()) values = list(dict_data.values()) line = plt.plot(years, values, marker='o', linewidth=2, markersize=4) # 線の最初(スタート地点)に国名ラベルを表示 plt.text(years[0], values[0], f'{country_names.get(code, code)} ', verticalalignment='center', horizontalalignment='right', fontsize=9, color=line[0].get_color(), fontweight='bold') plt.xlabel('Year', fontsize=11) plt.ylabel('Value (%)', fontsize=11) plt.title(f'{title} ({begin}-{end})', fontsize=13) plt.grid(True, alpha=0.3) plt.xticks(rotation=45) plt.tight_layout() # 画像として保存 plt.savefig(filename, dpi=300, bbox_inches='tight') plt.close() # 3つのグラフを生成 to_chart(df_1, list(country_names.keys()), 1997, 2024, "第一次産業 (Agriculture, forestry, and fishing)", "chart_primary.png") to_chart(df_2, list(country_names.keys()), 1997, 2024, "第二次産業 (Industry including construction)", "chart_secondary.png") to_chart(df_3, list(country_names.keys()), 1997, 2024, "第三次産業 (Services)", "chart_tertiary.png") 分析結果 第一次産業(農業・林業・漁業) ...

January 26, 2026 · 2 min

ラズパイ6台で作る、絶対に止まらない最強の自宅ネットワーク冗長化計画

はじめに 「自宅サーバーを構築したが、1台落ちただけで家族全員のネットが止まった」 そんな苦い経験(特にDNS/DHCP周りでの同期失敗)を経て、今回はRaspberry Pi 6台(+α)を駆使した「高可用性(HA)」に特化した自宅ネットワークを再設計します。 今回のコンセプトは「速度よりも、止まらないこと」。 北欧神話の神々の名を冠した6台のラズパイによる、3層の冗長化レイヤーを構築します。 1. ネットワーク全体像 上位ルーターとはWi-Fiで接続し、内部ネットワークは有線L2スイッチを中心に構成します。物理的に役割を分離することで、障害時の原因切り分けを容易にしています。 階層化の設計案 Edge層 (L1): インターネットへの門番。KeepalivedでゲートウェイIPを共有。 Core層 (L2): DHCPやDNS、認証など、NWの頭脳となる機能を同期。 Service層 (L3): ファイルサーバーなどの実データをレプリケーションして保持。 2. サーバー構成表:北欧神話の神々 物理筐体 ホスト名 役割 冗長化の仕組み Pi 3 (A) Odin 主系Gateway / VPN Keepalived (VIP: 192.168.1.1) Pi 3 (B) Frigg 副系Gateway / VPN Odinと仮想IPを共有 Pi 3 (C) Huginn DNS / DHCP Primary ISC-DHCP Failover / Gravity Sync Pi 3 (D) Muninn DNS / DHCP Secondary Huginnとリアルタイム同期 Pi 3 (E) Mjolnir ストレージ (NAS) GlusterFS + Keepalived (VIP: .200) Pi 3 (F) Gungnir ストレージ (NAS) Mjolnirとデータレプリケーション 3. 過去の失敗を防ぐ「守り」の技術選定 ① DNS/DHCP:仮想IPに頼らない 過去、DNSやDHCPを仮想IP(Keepalived)で制御しようとして失敗した経験から、今回はプロトコル標準の冗長化を採用します。 ...

January 24, 2026 · 1 min