はじめに
「もしもノート - 家族に伝える記録」という Android アプリを公開しました(買い切り・サーバー送信なしのエンディングノートアプリです)。
このアプリのコア機能が「パスワード付きPDFで家族に情報を渡す」なんですが、クラウド非経由・ローカル処理のみでこれをやろうとすると地味に詰まるポイントがあったので、実装の要点を共有します。
環境・前提
| 項目 | バージョン・詳細 |
|---|---|
| OS | Windows 11 |
| フレームワーク | Expo (SDK 54) + React Native |
| 言語 | TypeScript |
| 暗号化方式 | AES-128(パスワード付きPDF) |
問題(or 背景)
要件はシンプルで、「アプリ内のデータをPDFにして、パスワードを知っている家族だけが開けるようにする」でした。
ただし前提として、このアプリはデータをどこにも送信しない設計なので、PDF生成・暗号化も完全に端末内で完結させる必要があります。サーバー側でPDFを組み立てて返す、みたいな構成は最初から選択肢に入りませんでした。
原因(というか制約)
Expo のマネージドワークフローだと、ネイティブの暗号化PDFライブラリをそのまま使うのが難しい場面があります。今回は Expo のローカルモジュール機構でネイティブ処理を組み込む方針にしましたが、ここで実際にハマったのが以下の2点です。
-
ローカルモジュールの autolinking が効かない
modules/{name}/package.jsonとアプリ側package.jsonの両方にfile:./参照を書かないと autolinking がスキップされ、requireNativeModuleがnullを返す。片方だけだと「ビルドは通るが実行時に落ちる」という厄介な失敗モードになる。 -
podspec / AndroidManifest が無いとEASビルドに組み込まれない
ローカルの autolinking search 自体は「検出した」と出すのに、podspec(iOS)やAndroidManifest.xml(Android)が無いと EAS Build のバイナリに実体が入らない。ローカルモジュールとして機能させるには合計8ファイルほどを揃える必要がありました。
解決策・実装
ネイティブモジュール側の構成を最終的にこの形にしました(Android 側の例)。
modules/expo-pdf-encryptor/
├── package.json # file:./ 参照元
├── android/
│ ├── build.gradle
│ ├── src/main/AndroidManifest.xml
│ └── src/main/java/.../PdfEncryptorModule.kt
├── ios/
│ ├── ExpoPdfEncryptor.podspec
│ └── ExpoPdfEncryptor.swift
└── index.ts
Kotlin 側で地味に注意が必要だったのが、ファイルパスの扱いです。file:///... 形式のURIをそのまま File() に渡すとエンコード周りで誤解釈されるケースがあったため、java.net.URI 経由で正規化してから渡すようにしています。
val normalizedPath = java.net.URI(fileUri).path
val file = File(normalizedPath)
ポイント
- ローカルモジュールは「ファイルを置いた」だけでは動かない。
package.jsonのdependencies側参照・podspec・AndroidManifest まで揃っているかを実機ビルドで確認する - file URI はネイティブ側で直接
File()に渡さず、URIクラスで正規化する - 「サーバーに送らない」を前提にすると暗号化・PDF生成・保存まで全部端末内のネイティブ処理に閉じ込める必要があり、設計の自由度は下がるが説明文・プライバシーポリシーがシンプルになるメリットもある
まとめ
- ローカル完結のパスワード付きPDF出力は Expo のローカルモジュール機構で実装できるが、autolinking の罠が2箇所ある
- ファイルURIの扱いは Kotlin 側で
java.net.URI正規化を忘れずに - 暗号化方式は AES-128 を採用し、要件に対して十分な強度であることを確認したうえでリリースした
参考
- Expo Modules API ドキュメント
- 暗号化には PdfBox-Android(v2.0.27.0)の
StandardProtectionPolicyを使用(AES-128): https://github.com/TomRoush/PdfBox-Android
作ったアプリ
この記事の内容は「もしもノート - 家族に伝える記録」で使っています。