初めて ionic を使用してiosアプリを開発。jsonデータを ローカルストレージ に保存する機能を実装する必要があったので、手順を残す。
開発・実行環境
開発環境
・Windows10端末
実行環境
・ビルド : Mac Mini
・実行端末 : iPhoneSE2
依存関係 package.jsonから参照
- @angular/common – 12.1.1
- @ionic/angular – 5.5.2
- @ionic-native/core – 5.36.0
- @ionic-native/file – 5.36.0
- @capacitor/ios – 3.2.2
- cordova-plugin-file – 6.0.2
ios端末の ローカルストレージ にjsonファイルを書き込む
結論から言うと、書き込みのほうはすこし手間取ったが、比較的早い段階で動かすことができた。ionic Native File APIを使用して書き込みを行っていく。
必要パッケージをインストール
公式ページを参考にしながら必要なパッケージをインストールしていく。
作成したプロジェクトはCapacitorだったので、以下のコマンドを実行。
$ npm install cordova-plugin-file
$ npm install @ionic-native/file
$ ionic cap sync
ファイル保存プログラム実装
app.module.tsを編集
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { File } from '@ionic-native/file/ngx'; //追加する!!!
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
declarations: [
AppComponent,
],
entryComponents: [],
imports: [
BrowserModule,
HttpClientModule,
IonicModule.forRoot(),
AppRoutingModule,
IonicStorageModule.forRoot(),
],
providers: [
File, // 追加する!!!!
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
],
bootstrap: [AppComponent],
schemas: [
CUSTOM_ELEMENTS_SCHEMA
]
})
export class AppModule { }
書き込みのロジック部分を実装
公式ページを読むと保存できる場所はOSごとに分かれているらしい。今回は、ファイルの読み書きができてiosによって勝手にファイルを消されないdocumentsDirectoryを指定した。
import { Injectable } from "@angular/core";
import { File } from "@ionic-native/file/ngx";
@Injectable({
providedIn: 'root'
})
export class SamplFileService {
constructor(private file: File) {
super();
}
writeJsonFile(jsonObj: any) {
try {
const fileName: string = "sampleData.json";
this.file.writeFile(this.file.documentsDirectory, fileName, JSON.stringify(jsonObj)).then(() => {
console.log("書き込み完了");
}).catch(err => {
console.log(err);
});
} catch (e) {
console.log(e);
}
}
}
実機で動作確認(書き込み)
プログラムができたので、プロジェクトをビルドしてiosで動かす。このサイトを参考にさせていただいた。
Xcodeでinfo.plistを編集
しかし、ここで問題発生!先ほど書いた処理を実行しようとすると、急にアプリが落ちてしまった。どうやら、ローカルストレージにアクセスる場合、アプリの設定を変更する必要があるらしい。
Xcodeのプロジェクトのinfo.plistに以下を追加し、値をYESに設定する必要がある。
- Application supports iTunes file sharing
- Supports opening documents in place
こういう端末固有の設定は、ネイティブアプリ開発経験がないと全くわからない… 原因を調べていろいろ探っているうちにこのサイトに出会って解決した。ありがたや、ありがたや。
うまくいったなら実機のファイルアプリを開くと、自分の作った名前のアプリ名のフォルダがあるはず。そのフォルダ内にファイルが作成されている。
ios端末のテキストファイルを読み込む
やれやれ、何とかjsonファイルの書き込みができたので、読み込み処理を実装しようとしたが、これがドツボにはまってしまった。書き込みができるのだから、読み込むことぐらい簡単だろうと思ったが、罠が潜んでいたのだ。
読み込みのロジック部分の実装(問題あり)
公式サイトでファイルの読み込みを調べて、以下のような感じでプログラムを組んだ。
documentsDirectory 内にあるファイルのリストを取得して、先ほど作成したjsonファイルを読み込む処理を行っている。
async readFile() {
// jsonファイル検索
let fileEntry: Entry = undefined;
const path: string = this.file.documentsDirectory;
try {
const fileList: Entry[] = await this.file.listDir(path, '');
for (let f of fileList) {
if (f.isDirectory) { continue; }
if (f.name.includes(".json")) {
fileEntry = f;
break;
}
}
} catch (e) {
console.log(e);
return;
}
// ファイルが見つからない
if (fileEntry === undefined) {
console.log('jsonファイルが見つかりませんでした。');
return;
}
try {
const lines: string = await this.file.readAsText(this.file.documentsDirectory, fileEntry.name);
console.log("load json:" + lines);
} catch (e) {
console.log("jsonファイルの読み込みに失敗しました。");
}
}
そして、先ほどと同じように実機でデバックしたのだが、いつまでたっても、ファイルの内容がログに出力されない。最初はエラーが発生したと思ったのだが、エラーログも出力されていなかった。調べると、恐ろしいことが分かった!!
ionic Native FileのreadAsText()がいつまでたってもresolveされないバグ
どうやら、readAsText()の返り値のPromiseがいつまでたってもresolveされないバグがあるらしく実質ファイルの読み込みができない。読み込みができる人もいるのが厄介で、私の環境ではバグが発生した。ほかのreadAsDataURL()も同じバグがあるようだ。
googleで”ionic native file readastext don’t resolved“と調べると何件か出てくる。
- file.ReadAsText() doesn’t resolve a promise #3265 – GitHub
- Ionic-native-file can’t read file first time after save file – Stack …
解決方法に出てくるindex.htmlのスクリプト読み込み順番を以下のようにする方法をためにしても解決しなかった…
<script src="build/polyfills.js"></script>
<script src="cordova.js"></script>
ionic Native File以外のやり方でファイルを読み込む
Htmlのinputタグのfileを使う!
途方に暮れていた時あるサイトが目に留まった!!
IonicでOSに依存しないファイル選択・読み込み – Qiita
なんとhtmlのinputタグとjsのFileReaderを使ってファイルを読み込むことができたらしい。
てっきりiosはinputタグのfile読み込みは不可能と思い込んでいた。ちゃんと調べないと…
sample.html
<ion-app>
<ion-content>
<input type="file" (change)="test($event)"/>
</ion-content>
</ion-app>
sample.ts
private inputFile: any;
test(event) {
try {
this.inputFile = event.target.files[0];
this.inputFile.text().then(text => {
console.log(text);
});
} catch (e) {
console.log(e);
}
}
ブラウザで実行すると無事ファイルの内容が表示された。よしよし。では実機で動作確認だ!
エラー発生!! [DocumentManager] Failed to associate thumbnails for picked URL
[DocumentManager] Failed to associate thumbnails for picked URL file:///....Bourbon%20Chocolate%20Walnut%sampleData.json with the Inbox copy file:///....Bourbon%20Chocolate%20Walnut%sampleData.json: Error Domain=QLThumbnail Code=2 "(null)" UserInfo={NSUnderlyingError=0x149a042b0 {Error Domain=GSLibraryErrorDomain Code=3 "Generation not found" UserInfo={NSDescription=Generation not found}}}
調べるとどうやら開いたファイルのサムネイルをiosが作成しようとして失敗した際のエラーのようだ。
このサイトに書いてあった。サムネなんかいらない、求めていない…
まあ、エラーは出るがファイルを読み込むことができた!!