swiftコード

ヤフーショッピングapi json

{
	"ResultSet": {
		"0": {
			"Result": {
				"0": {
					"Name": "swift ブレーキパッド ≪type-SP (リアシュー)≫ 【bB [NCP31/NCP35(4WD)] 1500 ’00.1〜04.10】",
					"Description": "swift スウィフト スイフト ブレーキ ブレーキシュー シュー リヤシュー スーパースポーツ typeSP",
					"Headline": "スウィフトSuperSports リア用 左右セット",
					"Url": "https://store.shopping.yahoo.co.jp/auto-craft/swift-sp-r00059.html",
					"ReleaseDate": "",
					"Availability": "instock",
					"Code": "auto-craft_swift-sp-r00059",
					"Condition": "new",
					"Image": {
						"Id": "",
						"Small": "https://s.yimg.jp/images/sh/noimage/76x76.gif",
						"Medium": "https://s.yimg.jp/images/sh/noimage/146x146.gif"
					},
					"Review": {
						"Rate": "0.00",
						"Count": "0",
						"Url": "https://shopping.yahoo.co.jp/review/item/list?store_id=auto-craft&page_key=swift-sp-r00059"
					},
					"Affiliate": {
						"Rate": "1.0"
					},
					"Price": {
						"_attributes": {
							"currency": "JPY"
						},
						"_value": "11880"
					},
					"PremiumPrice": "",
					"PriceLabel": {
						"_attributes": {
							"taxIncluded": "true"
						},
						"FixedPrice": "",
						"DefaultPrice": "11880",
						"SalePrice": "",
						"PremiumPriceStatus": "0",
						"PremiumPrice": "11880",
						"PremiumDiscountType": "",
						"PremiumDiscountRate": "",
						"PeriodStart": "",
						"PeriodEnd": ""
					},
					"Point": {
						"Amount": "118",
						"Times": "1",
						"PremiumAmount": "118",
						"PremiumTimes": "1"
					},
					"Shipping": {
						"Code": "1",
						"Name": "設定無し"
					},
					"Category": {
						"Current": {
							"Id": "43160",
							"Name": "ブレーキシュー"
						}
					},
					"CategoryIdPath": {
						"0": {
							"Id": "1"
						},
						"1": {
							"Id": "2514"
						},
						"2": {
							"Id": "41234"
						},
						"3": {
							"Id": "43152"
						},
						"4": {
							"Id": "43160"
						},
						"_container": "Category"
					},
					"Brands": {
						"Name": "",
						"Path": {
							"0": {
								"Id": ""
							},
							"_container": "Brand"
						}
					},
					"JanCode": "4571498373717",
					"Model": "sp-VS2342",
					"IsbnCode": "",
					"Store": {
						"Id": "auto-craft",
						"Name": "オートクラフト",
						"Url": "https://store.shopping.yahoo.co.jp/auto-craft/",
						"Payment": {
							"0": {
								"Code": "1",
								"Name": "クレジットカード"
							},
							"1": {
								"Code": "16",
								"Name": "Yahoo!ウォレットに登録しているクレジットカード"
							},
							"2": {
								"Code": "2",
								"Name": "銀行振込"
							},
							"3": {
								"Code": "8",
								"Name": "郵便振替"
							},
							"4": {
								"Code": "2048",
								"Name": "Yahoo!マネー/預金払い"
							},
							"_container": "Method"
						},
						"IsBestStore": "false",
						"Ratings": {
							"Rate": "4.5",
							"Count": "6488",
							"Total": "29369",
							"DetailRate": "4.5"
						},
						"Image": {
							"Id": "auto-craft_1",
							"Medium": "https://item-shopping.c.yimg.jp/s/h/auto-craft_1"
						}
					},
					"IsAdult": "0",
					"Deliveryinfo": {
						"Area": "",
						"Deadline": "",
						"Day": ""
					},
					"_attributes": {
						"index": "4"
					}
				},
				"19": {
					"Name": "swift ブレーキパッド ≪type-SP (リアシュー)≫ 【ワゴンR [MH21S] 660 ’03.9〜08.9】 型式指定 12751",
					"Description": "swift スウィフト スイフト ブレーキ ブレーキシュー シュー リヤシュー スーパースポーツ typeSP",
					"Headline": "スウィフトSuperSports リア用 左右セット",
					"Url": "https://store.shopping.yahoo.co.jp/auto-craft/swift-sp-r00444.html",
					"ReleaseDate": "",
					"Availability": "instock",
					"Code": "auto-craft_swift-sp-r00444",
					"Condition": "new",
					"Image": {
						"Id": "",
						"Small": "https://s.yimg.jp/images/sh/noimage/76x76.gif",
						"Medium": "https://s.yimg.jp/images/sh/noimage/146x146.gif"
					},
					"Review": {
						"Rate": "0.00",
						"Count": "0",
						"Url": "https://shopping.yahoo.co.jp/review/item/list?store_id=auto-craft&page_key=swift-sp-r00444"
					},
					"Affiliate": {
						"Rate": "1.0"
					},
					"Price": {
						"_attributes": {
							"currency": "JPY"
						},
						"_value": "11880"
					},
					"PremiumPrice": "",
					"PriceLabel": {
						"_attributes": {
							"taxIncluded": "true"
						},
						"FixedPrice": "",
						"DefaultPrice": "11880",
						"SalePrice": "",
						"PremiumPriceStatus": "0",
						"PremiumPrice": "11880",
						"PremiumDiscountType": "",
						"PremiumDiscountRate": "",
						"PeriodStart": "",
						"PeriodEnd": ""
					},
					"Point": {
						"Amount": "118",
						"Times": "1",
						"PremiumAmount": "118",
						"PremiumTimes": "1"
					},
					"Shipping": {
						"Code": "1",
						"Name": "設定無し"
					},
					"Category": {
						"Current": {
							"Id": "43160",
							"Name": "ブレーキシュー"
						}
					},
					"CategoryIdPath": {
						"0": {
							"Id": "1"
						},
						"1": {
							"Id": "2514"
						},
						"2": {
							"Id": "41234"
						},
						"3": {
							"Id": "43152"
						},
						"4": {
							"Id": "43160"
						},
						"_container": "Category"
					},
					"Brands": {
						"Name": "",
						"Path": {
							"0": {
								"Id": ""
							},
							"_container": "Brand"
						}
					},
					"JanCode": "4571498374219",
					"Model": "sp-VS9967",
					"IsbnCode": "",
					"Store": {
						"Id": "auto-craft",
						"Name": "オートクラフト",
						"Url": "https://store.shopping.yahoo.co.jp/auto-craft/",
						"Payment": {
							"0": {
								"Code": "1",
								"Name": "クレジットカード"
							},
							"1": {
								"Code": "16",
								"Name": "Yahoo!ウォレットに登録しているクレジットカード"
							},
							"2": {
								"Code": "2",
								"Name": "銀行振込"
							},
							"3": {
								"Code": "8",
								"Name": "郵便振替"
							},
							"4": {
								"Code": "2048",
								"Name": "Yahoo!マネー/預金払い"
							},
							"_container": "Method"
						},
						"IsBestStore": "false",
						"Ratings": {
							"Rate": "4.5",
							"Count": "6488",
							"Total": "29369",
							"DetailRate": "4.5"
						},
						"Image": {
							"Id": "auto-craft_1",
							"Medium": "https://item-shopping.c.yimg.jp/s/h/auto-craft_1"
						}
					},
					"IsAdult": "0",
					"Deliveryinfo": {
						"Area": "",
						"Deadline": "",
						"Day": ""
					},
					"_attributes": {
						"index": "23"
					}
				},
				"Request": {
					"Query": "swift"
				},
				"Modules": "",
				"_container": "Hit"
			}
		},
		"totalResultsAvailable": "252014",
		"totalResultsReturned": 20,
		"firstResultPosition": "4"
	}
}

SearchItemTableViewController.swift

import UIKit

class SearchItemTableViewController: UITableViewController,
UISearchBarDelegate {
    var itemDataArray = [ItemData]()
    var imageCache = NSCache()
    var itemAllCount: String = "" // 自分で追加したコード
    // APIを利用するためのクライアントID
    let appid = "apiのid"
    let entryUrl: String =
    "https://shopping.yahooapis.jp/ShoppingWebService/V1/json/itemSearch"
    // 数字を金額の形式に整形するためのフォーマッター
    let priceFormat = NumberFormatter()
    override func viewDidLoad() {
        super.viewDidLoad()
        // 価格のフォーマット指定
        priceFormat.numberStyle = .currency
        priceFormat.currencyCode = "JPY"
        
    }
    
    // キーボードのsearchボタンがタップされたときに呼び出される
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        // 入力された文字の取り出し
        guard let inputText = searchBar.text else {
            // 入力文字なし
            return
        }
        // 入力文字数が0文字より多いかどうかチェックする
        guard inputText.lengthOfBytes(using: String.Encoding.utf8) > 0 else {
            // 0文字より多くはなかった
            return
        }
        // 保持している商品をいったん削除
        itemDataArray.removeAll()

        // パラメータを指定する
        let parameter = ["appid": appid, "query": inputText]
        // パラメータをエンコードしたURLを作成する
        let requestUrl = createRequestUrl(parameter: parameter)
        // APIをリクエストする
        request(requestUrl: requestUrl)
        // キーボードを閉じる
        searchBar.resignFirstResponder()
    }
    
    
    // パラメータのURLエンコード処理
    func encodeParameter(key: String, value: String) -> String? {
        // 値をエンコードする
        guard let escapedValue = value.addingPercentEncoding(
            withAllowedCharacters: CharacterSet.urlQueryAllowed) else {
                // エンコード失敗
                return nil
        }
        // エンコードした値をkey=valueの形式で返却する
        return "\(key)=\(escapedValue)"
    }
    
    // URL作成処理
    func createRequestUrl(parameter: [String: String]) -> String {
        var parameterString = ""
        for key in parameter.keys {
            // 値の取り出し
            guard let value = parameter[key] else {
                // 値なし。次のfor文の処理を行う
                continue
            }
            // すでにパラメータが設定されていた場合
            if parameterString.lengthOfBytes(using: String.Encoding.utf8) > 0 {
                // パラメータ同士のセパレータである&を追加する
                parameterString += "&"
            }
            // 値をエンコードする
            guard let encodeValue = encodeParameter(key: key, value: value)
                else {
                    // エンコード失敗。次のfor文の処理を行う
                    continue
            }
            // エンコードした値をパラメータとして追加する
            parameterString += encodeValue
        }
        let requestUrl = entryUrl + "?" + parameterString
        return requestUrl
    }
    
    // リクエストを行う
    func request(requestUrl: String) {
        // URL生成
        guard let url = URL(string: requestUrl) else {
            // URL生成失敗
            return
        }
        // リクエスト生成
        let request = URLRequest(url: url)
        // 商品検索APIをコールして商品検索を行う
        let session = URLSession.shared
        let task = session.dataTask(with: request) { (data:Data?,
            response:URLResponse?, error:Error?) in
            // 通信完了後の処理
            // エラーチェック
            guard error == nil else {
                // エラー表示
                let alert = UIAlertController(title: "エラー",
                                              message: error?.localizedDescription,
                                              preferredStyle: UIAlertController.Style.alert)
                // UIに関する処理はメインスレッド上で行う
                DispatchQueue.main.async {
                    self.present(alert, animated: true, completion: nil)
                }
                return
            }
            // JSONで返却されたデータをパースして格納する
            guard let data = data else {
                // データなし
                return
            }
            do {
                // パース実施
                let resultSet =
                    try JSONDecoder().decode(ItemSearchResultSet.self,
                                             from: data)
                // 商品のリストに追加
                self.itemDataArray.append(contentsOf:
                    resultSet.resultSet.firstObject.result.items)
                self.itemAllCount = resultSet.resultSet.totalResultsAvailable // 自分で追加したコード
                
                
                
            } catch let error {
                print("## error: \(error)")
            }
            // テーブルの描画処理を実施
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
        // 通信開始
        task.resume()
    }
    // MARK: - Table view data source
    // テーブルのセクション数を取得
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    // セクション内の商品数を取得
    override func tableView(_ tableView: UITableView,
                            numberOfRowsInSection section: Int) -> Int {
        return itemDataArray.count
    }
    // MARK: - Table view data source
    // テーブルセルの取得処理
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath:
        IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier:
            "itemCell", for: indexPath) as? ItemTableViewCell else {
                return UITableViewCell()
        }
        
        let itemData = itemDataArray[indexPath.row]
        
//print(itemAllCount)
        
        // 商品のタイトル設定
        cell.itemTitleLabel.text = itemData.name
        // 商品価格設定処理(日本通貨の形式で設定する)
        let number = NSNumber(integerLiteral: Int(itemData.priceInfo.price!)!)
        cell.itemPriceLabel.text = priceFormat.string(from: number)
        // 商品のURL設定
        cell.itemUrl = itemData.url
        
        cell.itemAllCountLabel.text = itemAllCount // 自分で追加したコード itemAllCountが取得できていない
        
        // 画像の設定処理
        // すでにセルに設定されている画像と同じかどうかチェックする
        // 画像がまだ設定されていない場合に処理を行う
        guard let itemImageUrl = itemData.imageInfo.medium else {
            // 画像なし商品
            return cell
        }
        // キャッシュの画像を取り出す
        if let cacheImage = imageCache.object(forKey: itemImageUrl as
            AnyObject) {
            // キャッシュ画像の設定
            cell.itemImageView.image = cacheImage
            return cell
        }
        // キャッシュの画像がないためダウンロードする
        guard let url = URL(string: itemImageUrl) else {
            // urlが生成できなかった
            return cell
        }
        
        let request = URLRequest(url: url)
        let session = URLSession.shared
        let task = session.dataTask(with: request) { (data:Data?,
            response:URLResponse?, error:Error?) in
            guard error == nil else {
                // エラーあり
                return
            }
            guard let data = data else {
                // データが存在しない
                return
            }
            guard let image = UIImage(data: data) else {
                // imageが生成できなかった
                return
            }
            // ダウンロードした画像をキャッシュに登録しておく
            self.imageCache.setObject(image, forKey: itemImageUrl as AnyObject)
            // 画像はメインスレッド上で設定する
            DispatchQueue.main.async {
                cell.itemImageView.image = image
            }
        }
        // 画像の読み込み処理開始
        task.resume()
        return cell
    }
    // 商品をタップして次の画面に遷移する前の処理
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let cell = sender as? ItemTableViewCell {
            if let webViewController =
                segue.destination as? WebViewController {
                // 商品ページのURLを設定する
                webViewController.itemUrl = cell.itemUrl
            }
        }
    }
}

temSearchResultSet.swift

import Foundation

// 検索結果全体を格納するクラス
class ItemSearchResultSet: Codable {
    var resultSet: ResultSet
    private enum CodingKeys: String, CodingKey {
        case resultSet = "ResultSet"
    }
}
// 検索結果セット格納クラス
class ResultSet: Codable {
    var firstObject: FirstObject
    var totalResultsAvailable: String = "" // 自分で追加したコード
    private enum CodingKeys: String, CodingKey {
        case firstObject = "0"
    }
}
// 検索結果の先頭を格納するクラス
class FirstObject: Codable {
    var result: Result
    private enum CodingKeys: String, CodingKey {
        case result = "Result"
    }
}


// 検索結果格納クラス
class Result: Codable {
    var items: [ItemData] = [ItemData]()
    required init(from decoder: Decoder) throws {
        // デコードのためのコンテナを取得
        let container = try decoder.container(keyedBy: CodingKeys.self)
        // コンテナ内のキーを取得。キーが文字列であるため、数値の昇順でソートも行う
        let keys = container.allKeys.sorted {
            Int($0.rawValue)! < Int($1.rawValue)!
        }
        // キーを使用して検索結果を一件ずつ取り出す
        for key in keys {
            // 検索結果一件に対するデコード処理
            let item = try container.decode(ItemData.self, forKey: key)
            // デコード処理できたら検索結果の一覧に追加
            items.append(item)
        }
        
    }
    // エンコード処理
    func encode(to encoder: Encoder) throws {
        // レスポンスを解析するだけなので、実装不要
    }
    // Resultクラスが持つ値を取得するためのキー
    private enum CodingKeys: String, CodingKey {
        case hit0 = "0"
        case hit1 = "1"
        case hit2 = "2"
        case hit3 = "3"
        case hit4 = "4"
        case hit5 = "5"
        case hit6 = "6"
        case hit7 = "7"
        case hit8 = "8"
        case hit9 = "9"
        case hit10 = "10"
        case hit11 = "11"
        case hit12 = "12"
        case hit13 = "13"
        case hit14 = "14"
        case hit15 = "15"
        case hit16 = "16"
        case hit17 = "17"
        case hit18 = "18"
        case hit19 = "19"
        case hit20 = "20"
    }
}

// 商品情報格納クラス
class ItemData: Codable {
    // 商品名
    var name: String = ""
    // 商品URL
    var url: String = ""
    // 商品画像情報
    class ImageInfo: Codable {
        // Imageクラスが持つ値を取得するためのキー
        private enum CodingKeys: String, CodingKey {
            case medium = "Medium"
        }
        // 商品画像URL
        var medium: String?
    }
    // 商品画像URL
    var imageInfo: ImageInfo = ImageInfo()
    // 価格情報
    class PriceInfo: Codable {
        // Priceクラスが持つ値を取得するためのキー
        private enum CodingKeys: String, CodingKey {
            case price = "_value"
        }
        // 価格
        var price: String?
    }
    // 価格
    var priceInfo: PriceInfo = PriceInfo()
    private enum CodingKeys: String, CodingKey {
        case name = "Name"
        case url = "Url"
        case imageInfo = "Image"
        case priceInfo = "Price"
    }
}

シェアする

  • このエントリーをはてなブックマークに追加

フォローする