JSONの解析
ゲームアプリやメッセージアプリなど、外部のサーバーとAPIでやり取りをするアプリは日々増えつつあります。APIを介したデータのやり取りで最も多く使われるフォーマットはJSONです。Swift 4ではJSONを解釈する際に、JSON内の各値を自動で構造体にマッピングできるCodableプロトコルが実装されました。
Codableプロトコルを実装した構造体を定義する【Swift 4】
JSONデータの例として、本記事ではランダムにユーザー情報を生成するRANDOM USER GENERATOR APIからの戻り値を利用します。RANDOM USER GENERATOR APIにブラウザからアクセスすると、下図の構造をしたJSONが確認できます。
resultsのキーの下層にユーザー情報が格納されています。
この構造のJSONを解析するために、JSONの構造に対応した2つの構造体を定義します。
- (1)ユーザー情報を配列で格納しているresultsに対する構造体
- (2)ユーザー情報を格納する構造体
(1)をResultListModel、(2)をPersonの名前で次の通り定義します。
// resultsを格納する構造体
struct ResultListModel:Codable{ // ------(1)
let results : [Person]
}
// ユーザー情報を格納する構造体
struct Person: Codable { // ------(2)
let gender: String
let name: Name // ------(3)
struct Name: Codable {
let title: String
let first: String
let last: String
}
let location: Location
struct Location: Codable {
let street: String
let city: String
let state: String
let postcode: Int
}
let email: String
# 略
}
ResultListModel構造体に関しては、JSON構造のresultsに対してユーザー情報を配列で持つ定義のみです(1)。Codableプロトコルを忘れないように実装しておきます。
Person構造体は、対応するJSONの構造の通りにプロパティを定義します(2)。StringやIntの場合は、通常のプロパティと同様に定義します。JSONの構造に階層がある場合は、その階層も構造体としてPerson構造体の中に定義します(3)。解析するJSONの構造とPerson構造体の関係は下図の通りです。
このように、Codableプロトコルを実装した構造体を定義しておきます。
JSONを解析する【Swift 4】
作成した2つの構造体を使って実際にJSONを解析します。APIへのアクセスに関してはSwift 3以前と同じく、URL→URLRequest→URLSession→URLSessionDataTaskの各クラスを利用して行います。
// RANDOM USER GENERATORのAPIにアクセスし、APIの結果をJSONで取得
let str : String = "https://randomuser.me/api"
let url : URL = URL(string: str)!
// URLRequestを生成してJSONのデータを取得
let request: URLRequest = URLRequest(url:url)
let session = URLSession.shared
let task : URLSessionDataTask = session.dataTask(with: request, completionHandler: {(data, response, error) in
// APIからの戻り値がなければ処理を終了
guard let responseData = data else{ return } // ------(1)
# 略
})
task.resume()
処理の進め方はリスト8のように、従来通りに行います。guard文による、APIからの戻り値がない場合の処理もSwift 3と同様です(1)。
guard文以降で、Swift 4から追加されたJSONDecoderクラスのdecodeメソッドを利用します。利用時の書式は次の通りです。
let decoder = JSONDecoder() let 構造体オブジェクト = try decoder.decode(構造体の名前, from: Dataオブジェクト)
書式の処理の内容は次の通りです。
- JSONDecoderクラスのインスタンスを生成
- Codableプロトコルを実装した構造体とDataオブジェクトを引数にdecodeメソッドを実行
この2行だけで、作成した構造体の通りにJSONのデータがマッピングされます。DataオブジェクトはAPIからの戻り値で、URLSessionDataTaskクラスから渡される値をそのまま利用できます。サンプルでの実際の利用例は次の通りです。
do { // ------(1)
// JSONDecoderクラスのインスタンスを生成
let decoder = JSONDecoder() // ------(2)
// JSONを解析して作成した構造体の通りにマッピング
let resultList = try decoder.decode(ResultListModel.self, from: responseData) // ------(3)
print(resultList)
// JSONを解析した後、Person構造体にマッピングされたデータを取り出す
for person in resultList.results { // 出力の例 // ------(4)
print(person.gender) // female
print(person.name.title) // madame
print(person.location.state) // ticino
print(person.email) // leana.aubert@example.com
}
} catch {
print("JSONの解析でエラーが起きました")
}
JSONDecoderクラスのdecodeメソッドは、throwsでエラーを投げる仕様になっているので、do-catch文の中に記述します。その中で、JSONDecoderクラスのインスタンスを生成(2)、decodeメソッドを実行します(3)。decodeメソッドに渡す変数は、解析するJSONの構造に対応したResultListModel構造体とAPIからの戻り値のresponseDataです。ResultListModel構造体には後ろにselfをつけて構造体自身をインスタンスとします。これでJSONのデータがオブジェクトにマッピングされます。
前項で定義したResultListModel構造体と、Person構造体のプロパティで解析したJSONのデータを取り出せます(4)。また、ResultListModel構造体のresultsプロパティでユーザー情報を格納したPersonの配列を参照できます。for文で回してPerson構造体の各プロパティで対応するJSONの値を参照することも可能です。
RANDOM USER GENERATOR APIの仕様上、呼び出すたびにランダムにユーザー情報が変わるので、サンプルを実行するたびに出力結果も変わります。
Swift 3以前では、APIから取得した戻り値をオブジェクトに変換したり、map関数やflatMap関数を何度も利用してJSON内の目的の値を取り出したりしていました。Swift 4では、これらの作業が必要なくなるだけでなく、JSONのフォーマットを構造体として定義できるため、JSONの書式が変わった際の対応なども容易にできます。
まとめ
今回はSwift 4で追加された、よく利用される便利な機能について説明しました。Swiftのバージョンアップに伴う新しい仕様は、いかに従来の処理を短縮化できるかに重点が置かれていると思われます。iOSやSwiftのバージョンアップが行われる際には、Apple Developerのドキュメントを確認して新しい機能を使ってみてください。
次回は動的な画面を作成するためによく利用される処理について説明します。
