Swift GoogleBook APIで書籍検索

作業フロー

1-エンドポイントの調査

2-API Keyの取得

3-jsonデータの分析

4-コード作成

5-iOSにてView作成

1-エンドポイント、(接続するアドレス)はここに記載されている

https://developers.google.com/books/docs/v1/using

具体的にはここ

https://www.googleapis.com/books/v1/volumes?q=flowers+inauthor:keyes&key=yourAPIKey

&keyのkey部分へ自分のキーを配置する

2-keyを取得してエンドポイントへアクセスする。するとこの様なjsonデータが返却される。

これを解析する。 今回はここに時間をかける。

{
 "kind": "books#volumes",
 "totalItems": 100,
 "items": [
  {
   "kind": "books#volume",
   "id": "_oG_iTxP1pIC",
   "etag": "fPjIOwmEaME",
   "selfLink": "https://www.googleapis.com/books/v1/volumes/_oG_iTxP1pIC",
   "volumeInfo": {  **//ここの配下のtitleとdescriptionを取得したい**
    "title": "Flowers for Algernon",
    "authors": [
     "Daniel Keyes"
    ],
    "publisher": "Houghton Mifflin Harcourt",
    "publishedDate": "2007-12-01",
    "description": "The beloved, classic story of a mentally disabled man whose experimental quest for intelligence mirrors that of Algernon, an extraordinary lab mouse.",
    "industryIdentifiers": [
     {
      "type": "ISBN_10",
      "identifier": "0547539630"
     },
     {
   

今回はtitleとdescriptionを取得することを目的とする。

構造体を宣言

欲しいものだけ最低限取得する。

型はエンコード、デコードできる、Codable型

まずitemsの取得、items配下は配列になっているのでItem型の配列とする。 nilになる可能性のないものはオプショナル型でなくても良い。

jsonデータ

"items": [ //ここで、配列ということがわかります。
  {
   "kind": "books#volume",
   "id": "_oG_iTxP1pIC",

Swiftでの記載

struct SearchResut: Codable {
    let kind:String
    let items:[Item] // Item型の配列
}

更にたどっていくと、volumeInfoの下に目的のtitleとdescriptionがある。
ここはjsonオブジェクトなので、配列ではない。
VolumeInfoはVolumeInfo型にする。このようにして、型だけを継承していくイメージ。
あくまでもイメージ。構造体は継承できない。

struct Item: Codable{
    let volumeInfo:VolumeInfo
    
}

VolumeInfoの下に目的のtitleとdescriptionがある。 それは、String型
これ以上の階層は追っかけない。

struct VolumeInfo: Codable{
    let title:String
    let description:String
}

これで、受け取る用意はできた。 あとはjsonの取得。

 //リクエストURL作成
        guard let req_url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=\(keyword_encode)") else{
            return books

nilになる可能性があるので、guard文にしておく。
httpの取得は、通信エラーなどでnilが返却されることがあるので注意が必要。

本当は非同期処理がいい。 NSURLSessionクラスを利用すれば、非同期ででききる 他にもAlamofireという非同期処理のライブラリがある。これがいいらしい。

cocoapods.org

エラーの可能性があるので, Swiftのエラー処理である、do~catchで囲む nilでも落ちないので便利。

static func searchBooks(keyword:String)->[BookInfo]{
   //返却したい型
   var books:[BookInfo] = []

   do{
           //urlをデータ型へ変換
            let data = try Data(contentsOf: req_url)  //これで非同期処理にならない。
            //jsonをデコード
            let searchResult = try JSONDecoder().decode(SearchResut.self, from: data)
            
            //デコードしたjsonを詰め込むsearchResultがトップなので、itemsはそのプロパティとして持っている
            for item in searchResult.items{
     //item配下のvolumeInfoを詰め込む
                let volumeInfo = item.volumeInfo
                //volumeInfo配下のtitleを詰め込む
                let title = volumeInfo.title
                //volumeInfo配下のdescriptionを詰め込む。同じ階層にある
                let description = volumeInfo.description
                
                //最後に返却したい型を別の箇所で宣言している。bookInfo型 下記に記載
                let bookInfo = BookInfo(title: title, description: description)
                //配列に詰め込んでいく
                    books.append(bookInfo)
                }
            
        }catch{
            print("エラーが出ました")
        }
        //BookInfo型の配列で返却
        return books
    }

bookInfo型

 struct BookInfo{
        var title:String
        var description:String?
    }

これで戻り値でほしい書籍データを取得できた。

必要ないと思うが,TableViewへの表示も記載しておく。

    //add
    var bookList:[BookInfo]?

  override func viewDidLoad() {
        super.viewDidLoad()
        
        //先程の関数 
        bookList = searchBooks(keyword: keyword!)
        
//tableViewのデリゲートメソッドを2つ記載しないとエラーになる。
   func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return  bookList!.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "bookCell", for: indexPath)
        cell.textLabel?.text = bookList![indexPath.row].title
        cell.textLabel?.font = UIFont.systemFont(ofSize: 15)
        return cell
    }