質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
Google API

Googleは多種多様なAPIを提供していて、その多くはウェブ開発者向けのAPIです。それらのAPIは消費者に人気なGoogleのサービス(Google Maps, Google Earth, AdSense, Adwords, Google Apps,YouTube等)に基づいています。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2474閲覧

iOSでGoogle Custom Searchを使いたい

bruteberry

総合スコア20

Google API

Googleは多種多様なAPIを提供していて、その多くはウェブ開発者向けのAPIです。それらのAPIは消費者に人気なGoogleのサービス(Google Maps, Google Earth, AdSense, Adwords, Google Apps,YouTube等)に基づいています。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2016/12/30 01:27

編集2017/01/05 06:37

###前提・実現したいこと
iOSアプリにて、wordImageという名前でOutlet接続したUIImageViewにGoogle Custom Searchエンジンを使用した検索結果の画像を表示したい。
wordImageArrayという名前のString配列に画像URLを格納し、ViewDidLoadでwordImageArray[0]を表示し、フリックで[1],[2],と次々に表示していこうと考えています。

###発生している問題・エラーメッセージ
うまくパースできない。

エラーメッセージ
格納したはずのwordImageArrayが空っぽ

###該当のソースコード

Swift

1 2import UIKit 3 4class SearchViewController: UIViewController { 5 6 @IBOutlet weak var searchLabel: UILabel! 7 @IBOutlet weak var wordImage: UIImageView! 8 9 //関連画像URLを格納する配列 10 var wordImageArray: [String] = [String]() 11 12 // APIを利用するためのアプリケーションID 13 let apikey: String = "??" 14 // let appid: String = "発行したアプリケーションIDをここに設定してください" 15 16 //APIを利用するためのサーチエンジンキー 17 let cx: String = "????" 18 19 //利用するAPIのサーチタイプ 20 let searchType: String = "image" 21 22 // APIのURL 23 let entryUrl: String = "https://www.googleapis.com/customsearch/v1" 24 25 var resultconfirm: String = "0" 26 27 override func viewDidLoad() { 28 super.viewDidLoad() 29 let pasteboard = UIPasteboard.general 30 let copiedText = pasteboard.string 31 searchLabel.text = copiedText 32 // Do any additional setup after loading the view. 33 34 35 // パラメータを指定する 36 let parameter = ["key": apikey,"cx":cx,"searchType":searchType,"q":copiedText] 37 38 39 // パラメータをエンコードしたURLを作成する 40 let requestUrl = createRequestUrl(parameter: parameter as! [String : String]) 41 42 print("リクエストURLは"+requestUrl) 43 44 // APIをリクエストする 45 request(requestUrl: requestUrl) 46 47 48// if let url = URL(string: wordImageArray[0]) { 49// let req = URLRequest(url: url) 50// let task = URLSession.shared.dataTask(with: req, completionHandler: {data, response, error in 51// if let data = data { 52// if let anImage = UIImage(data: data) { 53// DispatchQueue.main.async { 54// self.wordImage.image = anImage 55// } 56// } 57// } 58// }) 59// task.resume() 60// } 61 print(wordImageArray) 62 63 } 64 65 override func didReceiveMemoryWarning() { 66 super.didReceiveMemoryWarning() 67 // Dispose of any resources that can be recreated. 68 } 69 70 // パラメータのURLエンコード処理 71 func encodeParameter(key: String, value: String) -> String? { 72 // 値をエンコードする 73 guard let escapedValue = value.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else { 74 // エンコード失敗 75 return nil 76 } 77 // エンコードした値をkey=valueの形式で返却する 78 return "\(key)=\(escapedValue)" 79 } 80 81 // URL作成処理 82 func createRequestUrl(parameter: [String: String]) -> String { 83 var parameterString = "" 84 for key in parameter.keys { 85 // 値の取り出し 86 guard let value = parameter[key] else { 87 // 値なし。次のfor文の処理を行なう 88 continue 89 } 90 91 // 既にパラメータが設定されていた場合 92 if parameterString.lengthOfBytes(using: String.Encoding.utf8) > 0 { 93 // パラメータ同士のセパレータである&を追加する 94 parameterString += "&" 95 } 96 97 98 99 // 値をエンコードする 100 guard let encodeValue = encodeParameter(key: key, value: value) else { 101 // エンコード失敗。次のfor文の処理を行なう 102 continue 103 } 104 // エンコードした値をパラメータとして追加する 105 parameterString += encodeValue 106 107 } 108 let requestUrl = entryUrl + "?" + parameterString 109 return requestUrl 110 } 111 112 // 検索結果をパースして商品リストを作成する 113 func parseData(resultSet: [String: Any]) { 114 115 print(resultSet) 116 for key in resultSet.keys.sorted() { 117 // 関連画像取得 118 guard let result = resultSet[key] as? [String: Any] else { 119 // 次のfor文の処理を行なう 120 continue 121 } 122 print(result) 123 124 // レスポンスデータから画像の情報を取得する 125 let itemImageUrl = result["link"] as? String 126 127 // 配列に追加 128 wordImageArray.append(itemImageUrl!) 129 } 130 } 131 132 // リクエストを行なう 133 func request(requestUrl: String) { 134 // URL生成 135 guard let url = URL(string: requestUrl) else { 136 // URL生成失敗 137 return 138 } 139 140 // リクエスト生成 141 let request = URLRequest(url: url) 142 143 // APIをコールして商品検索を行なう 144 let session = URLSession.shared 145 let task = session.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in 146 // 通信完了後の処理 147 // エラーチェック 148 guard error == nil else { 149 // エラー表示 150 let alert = UIAlertController(title: "エラー", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert) 151 152 // UIに関する処理はメインスレッド上で行なう 153 DispatchQueue.main.async { 154 self.present(alert, animated: true, completion: nil) 155 } 156 return 157 } 158 159 // JSONで返却されたデータをパースして格納する 160 guard let data = data else { 161 // データなし 162 return 163 } 164 165 // JSON形式への変換処理 166 guard let jsonData = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: Any] else { 167 // 変換失敗 168 return 169 } 170 171 // データを解析 172 guard let resultSet = jsonData["items"] as? [String: Any] else { 173 // データなし 174 return 175 } 176 self.parseData(resultSet: resultSet) 177 } 178 // 通信開始 179 task.resume() 180 } 181 182} 183

###補足情報(言語/FW/ツール等のバージョンなど)
サーチエンジンキーとアプリケーションIDはすぐ実行できるように書いたままにしてありますが、悪用しないでください!!!><

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

JSONのデータ構造と違う解釈でアクセスしていたので取得できていないようです。

以下のように記述するとJSONデータの構造を知ることができます。

swift

1print(NSString(data: data!, encoding: String.Encoding.utf8.rawValue) ?? "")

変更した部分のみ載せますが、以下のようにすることで画像のURLを配列に入れることができます。

swift

1// 検索結果をパースして商品リストを作成する 2func parseData(items: [Any]) { 3 4 for item in items { 5 6 // レスポンスデータから画像の情報を取得する 7 guard let item = item as? [String: Any], let imageURL = item["link"] as? String else { 8 return 9 } 10 print(imageURL) 11 12 // 配列に追加 13 wordImageArray.append(imageURL) 14 } 15 16 dump(wordImageArray) 17} 18 19// リクエストを行なう 20func request(requestUrl: String) { 21 // URL生成 22 guard let url = URL(string: requestUrl) else { 23 // URL生成失敗 24 return 25 } 26 27 // リクエスト生成 28 let request = URLRequest(url: url) 29 30 // APIをコールして商品検索を行なう 31 let session = URLSession.shared 32 let task = session.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in 33 // 通信完了後の処理 34 35 print(NSString(data: data!, encoding: String.Encoding.utf8.rawValue) ?? "") 36 37 // エラーチェック 38 guard error == nil else { 39 // エラー表示 40 let alert = UIAlertController(title: "エラー", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert) 41 42 // UIに関する処理はメインスレッド上で行なう 43 DispatchQueue.main.async { 44 self.present(alert, animated: true, completion: nil) 45 } 46 return 47 } 48 49 // JSONで返却されたデータをパースして格納する 50 guard let data = data else { 51 // データなし 52 return 53 } 54 55 // JSON形式への変換処理 56 guard let jsonData = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: Any] else { 57 // 変換失敗 58 return 59 } 60 61 // データを解析 62 guard let resultSet = jsonData["items"] as? [Any] else { 63 // データなし 64 return 65 } 66 self.parseData(items: resultSet) 67 } 68 // 通信開始 69 task.resume() 70}

回答追記

swift

1 2override func viewDidLoad() { 3 super.viewDidLoad() 4 5 6 7 let pasteboard = UIPasteboard.general 8 var copiedText = pasteboard.string 9 searchLabel.text = copiedText 10 // Do any additional setup after loading the view. 11 12 copiedText = "dog" 13 14 // パラメータを指定する 15 let parameter = ["key": apikey,"cx":cx,"searchType":searchType,"q":copiedText] 16 dump(parameter) 17 18 // パラメータをエンコードしたURLを作成する 19 let requestUrl = createRequestUrl(parameter: parameter as! [String : String]) 20 21 print("リクエストURLは"+requestUrl) 22 23 // APIをリクエストする 24 request(requestUrl: requestUrl) { result in 25 26 dump(result) 27 if let url = URL(string: self.wordImageArray[0]) { 28 let req = URLRequest(url: url) 29 let task = URLSession.shared.dataTask(with: req, completionHandler: {data, response, error in 30 if let data = data { 31 if let anImage = UIImage(data: data) { 32 DispatchQueue.main.async { 33 self.wordImage.image = anImage 34 } 35 } 36 } 37 }) 38 task.resume() 39 } 40 } 41} 42 43override func didReceiveMemoryWarning() { 44 super.didReceiveMemoryWarning() 45 // Dispose of any resources that can be recreated. 46} 47 48// パラメータのURLエンコード処理 49func encodeParameter(key: String, value: String) -> String? { 50 // 値をエンコードする 51 guard let escapedValue = value.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else { 52 // エンコード失敗 53 return nil 54 } 55 // エンコードした値をkey=valueの形式で返却する 56 return "\(key)=\(escapedValue)" 57} 58 59// URL作成処理 60func createRequestUrl(parameter: [String: String]) -> String { 61 var parameterString = "" 62 for key in parameter.keys { 63 // 値の取り出し 64 guard let value = parameter[key] else { 65 // 値なし。次のfor文の処理を行なう 66 continue 67 } 68 69 // 既にパラメータが設定されていた場合 70 if parameterString.lengthOfBytes(using: String.Encoding.utf8) > 0 { 71 // パラメータ同士のセパレータである&を追加する 72 parameterString += "&" 73 } 74 75 76 77 // 値をエンコードする 78 guard let encodeValue = encodeParameter(key: key, value: value) else { 79 // エンコード失敗。次のfor文の処理を行なう 80 continue 81 } 82 // エンコードした値をパラメータとして追加する 83 parameterString += encodeValue 84 85 } 86 let requestUrl = entryUrl + "?" + parameterString 87 return requestUrl 88} 89 90// 検索結果をパースして商品リストを作成する 91func parseData(items: [Any], resultHandler: @escaping (([String]?) -> Void)) { 92 93 for item in items { 94 95 // レスポンスデータから画像の情報を取得する 96 guard let item = item as? [String: Any], let imageURL = item["link"] as? String else { 97 resultHandler(nil) 98 return 99 } 100 print(imageURL) 101 102 // 配列に追加 103 wordImageArray.append(imageURL) 104 } 105 106 resultHandler(wordImageArray) 107 dump(wordImageArray) 108} 109 110// リクエストを行なう 111func request(requestUrl: String, resultHandler: @escaping (([String]?) -> Void)) { 112 // URL生成 113 guard let url = URL(string: requestUrl) else { 114 // URL生成失敗 115 resultHandler(nil) 116 return 117 } 118 119 // リクエスト生成 120 let request = URLRequest(url: url) 121 122 // APIをコールして商品検索を行なう 123 let session = URLSession.shared 124 let task = session.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in 125 // 通信完了後の処理 126 127 print(NSString(data: data!, encoding: String.Encoding.utf8.rawValue) ?? "") 128 129 // エラーチェック 130 guard error == nil else { 131 // エラー表示 132 let alert = UIAlertController(title: "エラー", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert) 133 134 // UIに関する処理はメインスレッド上で行なう 135 DispatchQueue.main.async { 136 self.present(alert, animated: true, completion: nil) 137 } 138 resultHandler(nil) 139 return 140 } 141 142 // JSONで返却されたデータをパースして格納する 143 guard let data = data else { 144 // データなし 145 resultHandler(nil) 146 return 147 } 148 149 // JSON形式への変換処理 150 guard let jsonData = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: Any] else { 151 // 変換失敗 152 resultHandler(nil) 153 return 154 } 155 156 // データを解析 157 guard let resultSet = jsonData["items"] as? [Any] else { 158 // データなし 159 resultHandler(nil) 160 return 161 } 162 self.parseData(items: resultSet, resultHandler: resultHandler) 163 } 164 // 通信開始 165 task.resume() 166} 167

投稿2016/12/30 02:27

編集2016/12/31 10:20
_Kentarou

総合スコア8490

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bruteberry

2016/12/30 09:21

ありがとうございます!!!!助かりました。 dumpというのは便利ですね初めて知りました勉強になりますありがとうございます。 ですが、配列にアクセスしようとするとfatal error: Index out of rangeになってしまいます...。 Stringでキャストしてみたり、手動でappendしてみたりしましたが手動の方はうまくいきますので原因が分かりません。 もう少し自分で考えてみます。
_Kentarou

2016/12/30 12:10

どのような文字列(copiedText)で検索しているのですか? もし分からないようでしたらこちらでも調べます、通信しているので通信後に配列にアクセスするようにしてくださいね。
bruteberry

2016/12/31 09:06

返信ありがとうございます。 copiedTextはdogとかわかりやすいやつでやってみています。viewDidLoadの複数行コメントアウトしてあるところで配列の[0]にある文字列をとって画像を表示できるつもりなんですけども、うまくいきません。 print(wordImageArray[0])なんかもIndex out of rangeになってしまう状況です。 通信というのは複数行コメントアウトしてあるところですでに終わっているはずですよね?
_Kentarou

2016/12/31 09:15

> 通信というのは複数行コメントアウトしてあるところですでに終わっているはずですよね? 終わってないですよ、非同期で実行されているので、、、受信が終わったところで実行しないとダメです、もしくはクロージャーで処理を返すとかしないと。 以下でしている回答と同じ事です。 https://teratail.com/questions/60510
bruteberry

2016/12/31 09:47

申し訳ありません。 どうすれば通信を終わらせられるのか教えていただけないでしょうか
_Kentarou

2016/12/31 10:22

回答に追記しました。(画像が出るところまでは確認) 内容を理解していないと意味がないと思うので、なぜそうなっているのかを考えてみてください。
bruteberry

2017/01/04 06:52

遅くなって申し訳ありません 何から何まで本当にありがとうございました!!!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問