【かけ出しiPhoneアプリ開発】JSONデータの基本的な取り扱い



【かけ出しiPhoneアプリ開発】JSONデータの基本的な取り扱い

iPhoneアプリ開発をはじめたばかりの方にお届けする 【かけ出しiPhoneアプリ開発】 シリーズ。
今回は 『JSONデータの取り扱い方法』 をご紹介。

楽天や Yahoo!Japan、それから銀行系のオープンAPI など様々なデータが JSONデータで公開中。すぐに JSONデータを使ったアプリ開発とはいかないかもしれませんが、まずは JSONデータの取り扱い方法を学んで、将来の糧にして下さい。

本稿で使用する XCode のバージョン: XCode 10.1、 Swift 4.2

目次
  1. 【かけ出しiPhoneアプリ開発】JSONデータの基本的な取り扱い
  2. SwiftでJSONをはじめる前に知っておきたいこと
  3. JSONデータを読み込んでみよう
  4. JSONファイル内のデータをピックアップ
  5. 配列構造をもつJSONデータの読み込み(基本)
  6. JSONデータの階層トップ部分を読み込み
  7. JSONデータが不揃いな場合
  8. まとめ

【かけ出しiPhoneアプリ開発】JSONデータの基本的な取り扱い

SwiftでJSONをはじめる前に知っておきたいこと

image

Swiftベースのアプリ開発は、 XCodeの助けもあってか比較的スムーズに開発を進めることができると思います。しかし、 JSON は今までとは違った学習ストレスが。アプリ開発のモチベーション低下を避ける意味でも、 Swiftで JSONを取り扱う前に知っておきたいことをお知らせします。

データ型

image

これから JSONデータを取り扱うのですが、まず上図のコード、どちらが JSON か分かりますでしょうか?もし悩まれたのなら、もう一度 Swift の「配列」と「辞書」を復習することをオススメします。

正解は、上のコードは Swift の辞書型で、下のコードは JSONコードになります。このように Swift の「辞書」とJSONデータ、非常に似ていますが、まったく異なるデータ型。まずは Swiftの辞書型と JSONデータを混同しないように気をつける必要があります。

XMLとJSON

image

動的な情報を持つデータ型としては、 JSON 以外に XML というデータ型もあります。XMLは、ブログサイトの RSS フィードの際によく使われるデータ型ですよね。また楽天のAPIや Yahoo!Japan の API などでは、 JSON と XML 両方が公開中。 Amazonについては XML のみです。

もし仮に扱おうとしているデータが、絶対 JSON でないといけないっ!、というわけでないのなら、 XML でデータを扱うことをオススメします。初歩的なデータの扱いについては、JSONに比べて XML の方が、比較的簡単です。しかし、金融系の API をはじめ、やっぱり JSON を Swift で扱う必要があるっ!、という場合は頑張って JSON の取り扱い方法を学ぶ必要があるでしょう。

また参考までに JSON と XML の違いを確認。

比較項目 JSON XML
汎用性
タグ
処理スピード
配列管理
データ解析

難易度

image

img: iOS Swift 4 Tutorial JSON Downloading into TableView ft iPhone X in Xcode 9

JSONデータを使ってリスト表示するチュートリアルはあるものの・・・・実際にやってみるとかなり難しいです!!!

「JSONデータを使って、商品一覧を表示させたい」「JSONデータを使って、データ検索したい」など JSONデータを使って何をするかは人それぞれと思います。しかし、 JSONデータを使ったアプリ開発は、これまでのアプリ開発よりも 2倍、3倍難しいかもしれません。 難しい理由としては、

  • チュートリアルが少ない
  • 柔軟性の高いデータ型故に、取り扱い方法がちょっと難しい
  • 配列型や辞書型と混同してしまう
  • プログラミング段階ではエラーが分かりにくい
  • struct{} や do 、 try に guard などレベルの高い Swift が必要

「前に XMLデータを使った RSSアプリを作ったことあるから大丈夫」という気持ちで JSON を扱おうとすると、失敗するかもしれませんよ。

JSONデータを見やすくしよう

image

まずはじめに JSONファイル を開いた時、そのデータ、見やすいでしょうか?まずはこれから JSON データを扱っていく上で、 JSONを見やすいように表示する必要があります。

【JSONデータを見やすくする方法】

  • ブラウザ Firefox で JSONファイルを開く
  • Chromeの拡張機能を使う
  • Webサービスを利用する
  • テキストエディタのプラグインを使う 等

Chrome の拡張機能についてはいくつかありますが、私は 「JSONView」 を使っています。 Webサービスについては、 こちら が見やすいです。テキストエディタについては、 atom-json-editor などお使いのエディタに合わせたプラグインを検索する必要が。JSONデータの多くは、URLで管理されている場合が多いので、ブラウザベースで JSONデータを見やすくしておくことを勧めますね。

それでは実際に Swift を使って、 JSONデータを確認していきましょう。


JSONデータを読み込んでみよう

image

まずはじめに Swift で JSON ファイルを読み込むところからはじめてみましょう。JSONデータを取り扱う以前に、データを読み込めているかどうかって重要ですよね。今回扱う JSONファイルは上図の様に簡単なものとしました。実際に下記リンクより JSONデータを確認してみて下さい。

JSONファイル

このデータを Swift(XCode) を使って読み込むには、以下のようなプログラムに。

import UIKit
//
class ViewController:UIViewController{
    override func viewDidLoad() {
        super.viewDidLoad()
        //
        let jsonUrlString = "https://pythonchannel.com/media/codecamp/201902/JSON-Sample1.json"
        guard let url = URL(string: jsonUrlString) else {return}
        //      
        URLSession.shared.dataTask(with: url) {(data, response, error) in
            guard let data = data else {return}
            print(data)
            //
        }.resume()
    }
}

XCodeを起動して、 「Create a New Xcode project」 から 「Single View App」 そして プロジェクト名を入力して、 ViewController.swift に上記コードをコピー&ペーストしてみて下さい。

image

シミュレーターを起動すると、XCode下のデバグエリアに 285 bytes と表示されますね。それでは JSONデータが 285 bytes として読み込めたプログラムを確認していきましょう。

まず JSON ファイルを定数 jsonUrlString に格納。この jsonUrlString は任意の名前です。そして guard文を使って先ほどの jsonUrlStringの処理を進捗。


【guard文について】

guard文は、 if文のように条件によって処理工程を変えたい時やオプショナル型の関係で必要となる時に使用。

-- サンプルコード --


func hello(time:Int) {
    guard ( time < 10 ) else {
        print("こんにちは")
        return
    }
    print("おはようございます")
}
hello(time:13)

time:〇〇 の値によって出力が変化。上記の場合は、 time:13 なので「こんにちは」が出力。


当然 url = URL(string: jsonUrlString) の条件式は満たされないので、次の URLSession.shared.dataTask(wi・・・ に処理を進捗。

  • URLSession URLからデータをダウンロード
  • shared シングルトンの URLSessionインスタンス、shared以外には default や background など
  • dataTask() URLからコンテンツを取得するインスタンス・メソッド。(with: url) {(data, response, error)は dataTask()のパラメーター。 data, response, error は基本的な定型文。

そして guard let data = data は満たされないので、 print() に進み、 .resume() でタスク実行。その結果が、 285 bytes になります。つまり JSONデータを読み込めていることが確認できますね。次は JSONファイルの中身をピックアップしてみたいと思います。


JSONファイル内のデータをピックアップ

image

Swiftで JSONファイルの読み込みに成功したら、今度は JSONファイル内の値を抽出したいですよね。Swiftで JSONファイル内の値を抽出する方法はいくつかパターンがありますが、今回は struct(構造文) を使ったケースでご紹介。

【SwiftでJSONファイル内の値を抽出するコード】

import UIKit
//
struct Blog: Decodable{
    let id: Int
    let name: String
    let link: String
    let imageUrl: String
}
//
class ViewController:UIViewController{
    override func viewDidLoad() {
        super.viewDidLoad()
        //
        let jsonUrlString = "https://pythonchannel.com/media/codecamp/201902/JSON-Sample1.json"
        guard let url = URL(string: jsonUrlString) else {return}
        //
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else {return}
            //
            do{
                let myblog = try JSONDecoder().decode(Blog.self, from: data)
                print(myblog.name)
            } catch let jsonError{
                print("error", jsonError)
            }
            }.resume()
    }
}

前章のコードに似ていますが、 大きく 2カ所変更点があります。

image

まず最初に追加された struct ですが、これは Swift4 で導入された プロトコルの Decodable を使うため。 struct Blog: Decodable{ この文については、 Blog だけが任意の値で、 struct と Decodable は定型文。そして構造内(struct)のプロパティは、JSONデータと同じキーを使用。例えば、 JSONデータ内では id とあるのに、Swift側で ID とするとエラーがでます。また基本的にはJSONデータ内のキーを全て記述する必要がありますね。

そしてもう一つの変更点 do{ ・・・ 。do文、例外処理の時に使う文法の一つでしたね。 JSONデータを扱う際は、エラーが発生しやすくなりますので、 do文を使うのが一般的です。

上記コードの場合、 do文を用いることで JSONDecoder() が上手くいけば print() で結果出力、上手くいかなければ error を出力するようにセットされています。

  • JSONDecoder() JSONデータを Swiftの構造体に転換するクラス
  • decode() JSONデータをデコードするメソッド。上記の場合は、定数 data を 構造体 Blog にデコード。

そして JSONデータが入った構造体 Blog 内の name を print(myblog.name) で出力。もし何かの理由で上手くデコードできなければ error が表示。 print(myblog.name) の name を id や link に変えると、それぞれの値を取得できるでしょう。


配列構造をもつJSONデータの読み込み(基本)

image

今度は、配列を用いたJSONファイルの読み込みにチャレンジ。

配列構造をもつ JSONデータ

JSONファイル内には 3ブロックのデータをもち、キーは前章までと同じ id、 name、 link、 imageUrl であることが確認できます。このような配列構造のデータを読み込む場合は、 decode() の構造体を配列にすれば OK。

image


【配列構造をもつ JSONデータの読み込みSwiftプログラム】

import UIKit
//
struct Blog: Decodable{
    let id: Int
    let name: String
    let link: String
    let imageUrl: String
}
//
class ViewController:UIViewController{
    override func viewDidLoad() {
        super.viewDidLoad()
        //
        let jsonUrlString = "https://pythonchannel.com/media/codecamp/201902/JSON-Sample2.json"
        guard let url = URL(string: jsonUrlString) else {return}
        //
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else {return}
            //
            do{
                let myblogs = try JSONDecoder().decode([Blog].self, from: data)
                print(myblogs)
            } catch let jsonError{
                print("error", jsonError)
            }
            }.resume()
    }
}

decode() 内のプロパティを [Blog] とすることで、配列型の JSONデータを読み込めていますね。



JSONデータの階層トップ部分を読み込み

image

JSONファイル

APIなどで使われている JSONファイルの多くは上図のようなデータタイトルを所有。この階層トップ部分を読み込みたい時、もしくは階層内のデータを扱いたい時は構造体をもう一つ設ける必要があります。

image

新しく struct Website を設け、 Website内の構造体には JSONデータ同様の name と description をプロパティとしてセット。そして decode() 部分のプロパティを今までの Blog から Website に変更。これで JSONファイルの階層トップ部分をピックアップできます、


【JSONデータの階層トップ部分を読み込むSwiftプログラム】

import UIKit
//
struct Website: Decodable{
    let name: String
    let description: String
    let posts: [Blog]
}
struct Blog: Decodable {
    let id: Int
    let name: String
    let link: String
    let imageUrl:String
}
//
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        //
        let jsonUrlString = "https://pythonchannel.com/media/codecamp/201902/JSON-Sample3.json"
        guard let url = URL(string: jsonUrlString) else {return}
        //
        URLSession.shared.dataTask(with: url) { (data, response, error) in
        //
            guard let data = data else {return}
            //
            do{
                let siteInfo = try JSONDecoder().decode(Website.self, from: data)
                print(siteInfo.name, siteInfo.description)
            //
            } catch let jsonError{
                print("error", jsonError)
            }
        }.resume()
    }
}

こうした階層をもつ JSONファイルは普通ですが、トップ階層部分以外の配列内のデータを抽出しようと思うと、タプル配列にデータを格納するなど難しいプログラムが必要に。今回は文量的に割愛させて頂きます。


JSONデータが不揃いな場合

image

JSONファイル

楽天APIなど商品データを扱う JSONファイル、時にはキーに対して値がない場合もあります。そうした場合、Swiftコードを従来のような書き方で処理するとエラーがでます。この問題は、構造体内のプロパティをオプショナル型にすれば OK。 nil を許容してくれます。

image


【JSONデータが不揃いな場合のSwiftプログラム】

import UIKit
//
struct Blog: Decodable{
    let id: Int?
    let name: String?
    let link: String?
    let imageUrl: String?
}
//
class ViewController:UIViewController{
    override func viewDidLoad() {
        super.viewDidLoad()
        //
        let jsonUrlString = "https://pythonchannel.com/media/codecamp/201902/JSON-Sample4.json"
        guard let url = URL(string: jsonUrlString) else {return}
        //
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            guard let data = data else {return}
            //
            do{
                let myblogs = try JSONDecoder().decode([Blog].self, from: data)
                print(myblogs)
            } catch let jsonError{
                print("error", jsonError)
            }
            }.resume()
    }
}


まとめ

プログラミング初心者の方の多くは JSONデータに慣れていないと思います。私も最初の内は、XCodeで同じコードを書いているのにエラーがでて困ったり、 JSONデータをいつの間にか Swiftの辞書型と同じと勘違いしたり、思ったような JSONデータを抽出できずイライラしたり。しかし、5回 6回と Swiftコードを書いていく内にいろいろ気づきもあり、段々 JSONデータが使えるように。

以上のことから Swiftで JSONデータを扱う時に重要なことは、「モチベーション」と「学習環境」でしょう。エラーを解決できない時、恐らく相当な時間消費とモチベーション低下が想定されます。こうした問題はプログラミング・スクールに頼ってみるのも一手ではないでしょうか。

中でもオンライン型のプログラミング・スクールは、「場所」と「時間」の制約を極力減らし、今のライフスタイルに馴染みやすいとのこと。「オンライン型のレッスン」まだの方は、一度お試しいかがでしょう? CodeCamp では即日からお試し可能です。まずは公式ページから様子を確認してみませんか?

オシママサラ
この記事を書いた人
オシママサラ
\ 無料体験開催中!/自分のペースで確実に習得!
オンライン・プログラミングレッスンNo.1のCodeCamp