プロジェクトにリソースとしてバンドルされたJSONファイルを読み込む方法を解説します。
リソースファイルではなく、アプリ内のファイルシステムにあるファイルにアクセスしたい場合は、【Swift】アプリ内のファイル読み書き も合わせてご覧下さい。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.2.4
【iOS】13.6
【macOS】Catalina バージョン 10.15.5
JSONファイルの準備
次のような従業員情報を格納した"employee.json"を準備します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
[ { "age" : 45, "code" : "001", "name" : "山田", "absence" : false }, { "age" : 30, "code" : "002", "name" : "鈴木", "absence" : true }, { "age" : 25, "code" : "003", "name" : "佐藤", "absence" : false } ] |
下図のようにファイルをドラッグしてProject Navigatorに追加します。
サンプルソースと解説
情報を格納するEmployee構造体は、JSON形式に合わせて次のようにします。
Codableプロトコル準拠とします。
1 2 3 4 5 6 7 8 |
struct Employee: Codable { var code: String // 社員コード var name: String // 名前 var age: Int // 年齢 var absence: Bool // 休暇中フラグ } |
プロジェクトに追加したemployee.jsonファイルを読み込む部分のコードがこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/// ①プロジェクト内にある"employees.json"ファイルのパス取得 guard let url = Bundle.main.url(forResource: "employees", withExtension: "json") else { fatalError("ファイルが見つからない") } /// ②employees.jsonの内容をData型プロパティに読み込み guard let data = try? Data(contentsOf: url) else { fatalError("ファイル読み込みエラー") } /// ③JSONデコード処理 let decoder = JSONDecoder() guard let employees = try? decoder.decode([Employee].self, from: data) else { fatalError("JSON読み込みエラー") } /// ④データ確認 for employee in employees { print(employee) } |
①ファイルのパスを取得
Xcodeでビルドされたアプリでは「バンドル(Bundle)」と呼ばれるものが作成され、プロジェクトに追加したファイルはこのBundle内に配置されます。
Bundle.main.url()を使って、このBundle内に格納された対象ファイルのパスを取得します。
引数(forResouece)にはファイル名、引数(withExtension)に拡張子を指定します。
対象ファイルが存在しない場合はnilが戻りますので、サンプルではfatalError()で強制的にアプリをクラッシュさせています。
②ファイルの読み込み
Data(contentsOf :)を使用して、ファイルの内容をData型にロードします。
このメソッドは例外をスローする可能性があるので、tryを付けて呼び出します。
この例ではtry?を使っているので、例外自身は無視されますが、戻り値がnullになりますのでその場合はfatalError()が発生します。
③JSONデコード処理
JSONデータをデコードし、オブジェクト化します。
④データ確認
オブジェクト化したデータの表示処理です。
出力結果
1 2 3 4 5 |
Employee(code: "001", name: "山田", age: 45, absence: false) Employee(code: "002", name: "鈴木", age: 30, absence: true) Employee(code: "003", name: "佐藤", age: 25, absence: false) |
JSONファイル読み込み処理を汎用化する
Bundleに拡張機能を追加して、JSONファイル読み込みを汎用化すると便利です。
次のコードをプロジェクトに追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
extension Bundle { func decodeJSON<T: Codable>(_ file: String) -> T { guard let url = self.url(forResource: file, withExtension: nil) else { fatalError("Faild to locate \(file) in bundle.") } guard let data = try? Data(contentsOf: url) else { fatalError("Failed to load \(file) from bundle.") } let decoder = JSONDecoder() guard let loaded = try? decoder.decode(T.self, from: data) else { fatalError("Failed to decode \(file) from bundle.") } return loaded } } |
ポイントはSwiftのジェネリック機能を使い、取得したJSONデータを格納するオブジェクトの形式も汎用化している点です。
次のように呼び出すだけで、JSONファイルの読み込みができます。
1 2 3 |
let employees: [Employee] = Bundle.main.decodeJSON("employees.json") |