LoginSignup
20
20

More than 5 years have passed since last update.

Google画像検索APIをiOSで利用する

Last updated at Posted at 2017-02-20

完成品
スクリーンショット 2017-02-20 10.38.37.png
Searchボタンで画像を表示!
左右にSwipeで画像を切り替える!

※この記事では、Xcode8.2、Swift3を使用しています。


まず、適当な名前をつけてSingle View Applicationのプロジェクトを作成します。

スクリーンショット 2017-02-19 9.33.09.png

ImageView、TextField、Buttonを適当に配置。
Buttonをダブルクリックダブルクリックして名前をSearchとかに変えておきます。
スクリーンショット 2017-02-19 9.35.33.png

次に、Google画像検索をするために必要な
・APIキー
・検索エンジンキー
を取得する必要があります。

まずAPIはこちらから取得します。
https://console.developers.google.com/?hl=JA

プロジェクトを作成し、認証情報を作成して、APIを有効化するという手順のようです。
認証情報→プロジェクト作成→認証情報作成で、APIキーが作成されます。
スクリーンショット 2017-02-19 9.50.02.png
キーの制限は無くても大丈夫みたいです。

次に検索エンジンキーを取得します。
https://cse.google.com/cse/
にて、Google Custom Search Engineを作成します。
Addをクリックして作成してください。
設定はこんな感じで良いと思います。画像検索をオンにするのを忘れずに。
詳細→検索エンジンIDをクリックで検索エンジンIDを取得できます。
スクリーンショット 2017-02-19 16.29.10.png


さて、必要なものは揃ったのでアプリを作っていきましょう。
まずは先程配置したImageViewとTextFieldを、Storyboardからコントロール+ドラッグ&ドロップでコードのほうに引っ張って、適当な名前をつけてOutlet接続します。
ButtonのほうはAction接続をし、タップされた時の処理を{}内に書いていけるようにします。

ViewController.swift
import UIKit

class ViewController: UIViewController {
   @IBOutlet weak var image: UIImageView!
   @IBOutlet weak var imageTextField: UITextField!
   @IBAction func searchButtonTapped(_ sender: Any) {
   }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

ここで、画像をスワイプで切り替えれるようにするための下準備をします。

Swipe Gesture Recognizerを2つ、ImageViewの上にドラッグ&ドロップし、Attribute Inspectorで片方をRight、もう片方をLeftに設定し、コード部にCtrl+ドラッグ&ドロップで適当な名前をつけてAction接続します。

スクリーンショット 2017-02-19 21.23.57.png

ImageViewは、デフォルトではタッチが効かないようになっているため、Attribute Inspector→View→Interactionの、User Interaction Enabledにチェックを入れましょう。
スクリーンショット 2017-02-19 21.42.52.png

また、画像がImageViewの正方形の中に、縦横比はそのままで、大きく表示されるように、Content ModeをAspect Fitに設定しておきます。
スクリーンショット 2017-02-20 11.27.17.png

準備はできたので、残りのプログラムを書きます

ViewController.swift

import UIKit

class ViewController: UIViewController,UITextFieldDelegate{

    @IBAction func rightSwiped(_ sender: Any) {
        if(imageSub != 0){
            imageSub = imageSub-1
        }
        if let url = URL(string: wordImageArray[imageSub]) {
            let req = URLRequest(url: url)
            let task = URLSession.shared.dataTask(with: req, completionHandler: {data, response, error in
                if let data = data {
                    if let anImage = UIImage(data: data) {
                        DispatchQueue.main.async {
                            self.image.image = anImage
                        }
                    }
                }
            })
            task.resume()
        }
    }
    @IBAction func leftSwiped(_ sender: Any) {
        //imageSubを1つすすめる
        imageSub = imageSub+1

        //画像配列の末尾に到達している場合、配列に新たな10件を登録する
        if((imageSub) == wordImageArray.count){

            //パラメータのstartを決める
            let startPara: String = String(imageSub)

            //検索ワード
            let pasteboard = UIPasteboard.general
            let copiedText = pasteboard.string

            // パラメータを指定する
            let parameter = ["key": apikey,"cx":cx,"searchType":searchType,"q":copiedText,"start":startPara]

            // パラメータをエンコードしたURLを作成する
            let requestUrl = createRequestUrl(parameter: parameter as! [String : String])

            // APIをリクエストする
            request(requestUrl: requestUrl) { result in
                if let url = URL(string: self.wordImageArray[self.imageSub+1]) {
                    let req = URLRequest(url: url)
                    let task = URLSession.shared.dataTask(with: req, completionHandler: {data, response, error in
                        if let data = data {
                            if let anImage = UIImage(data: data) {
                                DispatchQueue.main.async {
                                    self.image.image = anImage
                                }
                            }
                        }
                    })
                    task.resume()
                }
            }
        }else{
            if let url = URL(string: wordImageArray[imageSub]) {
                let req = URLRequest(url: url)
                let task = URLSession.shared.dataTask(with: req, completionHandler: {data, response, error in
                    if let data = data {
                        if let anImage = UIImage(data: data) {
                            DispatchQueue.main.async {
                                self.image.image = anImage
                            }
                        }
                    }
                })
                task.resume()
            }
        }
    }
    @IBOutlet weak var image: UIImageView!
    @IBOutlet weak var imageTextField: UITextField!

    // APIを利用するためのアプリケーションID
    let apikey: String = "*********************"

    //APIを利用するためのサーチエンジンキー
    let cx: String = "*********************"

    //利用するAPIのサーチタイプ
    let searchType: String = "image"

    // APIのURL
    let entryUrl: String = "https://www.googleapis.com/customsearch/v1"

    //関連画像URLを格納する配列
    var wordImageArray: [String] = [String]()

    //現在表示している画像の添字を格納する変数
    var imageSub :Int = 0;


    @IBAction func searchButtonTapped(_ sender: Any) {
        let query = imageTextField.text
        //配列の要素全削除
        wordImageArray.removeAll()
        // パラメータを指定する
        let parameter = ["key": apikey,"cx":cx,"searchType":searchType,"q":query]

        // パラメータをエンコードしたURLを作成する
        let requestUrl = createRequestUrl(parameter: parameter as! [String : String])

        // APIをリクエストする
        request(requestUrl: requestUrl) { result in
            if let url = URL(string: self.wordImageArray[0]) {
                let req = URLRequest(url: url)
                let task = URLSession.shared.dataTask(with: req, completionHandler: {data, response, error in
                    if let data = data {
                        if let anImage = UIImage(data: data) {
                            DispatchQueue.main.async {
                                self.image.image = anImage
                            }
                        }
                    }
                })
                task.resume()
            }
        }
    }



    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        // textFiel の情報を受け取るための delegate を設定
        imageTextField.delegate = self

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    func textFieldShouldReturn(_ textField: UITextField) -> Bool{
        // キーボードを閉じる
        textField.resignFirstResponder()

        return true
    }

    // パラメータの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 parseData(items: [Any], resultHandler: @escaping (([String]?) -> Void)) {

        for item in items {

            // レスポンスデータから画像の情報を取得する
            guard let item = item as? [String: Any], let imageURL = item["link"] as? String else {
                resultHandler(nil)
                return
            }
            print(imageURL)

            // 配列に追加
            wordImageArray.append(imageURL)
        }

        resultHandler(wordImageArray)
    }

    // リクエストを行なう
    func request(requestUrl: String, resultHandler: @escaping (([String]?) -> Void)) {
        // URL生成
        guard let url = URL(string: requestUrl) else {
            // URL生成失敗
            resultHandler(nil)
            return
        }

        // リクエスト生成
        let request = URLRequest(url: url)

        // APIをコールして検索を行う
        let session = URLSession.shared
        let task = session.dataTask(with: request) { (data:Data?, response:URLResponse?, error:Error?) in
            // 通信完了後の処理
            print(NSString(data: data!, encoding: String.Encoding.utf8.rawValue) ?? "")

            // エラーチェック
            guard error == nil else {
                // エラー表示
                let alert = UIAlertController(title: "エラー", message: error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)

                // UIに関する処理はメインスレッド上で行なう
                DispatchQueue.main.async {
                    self.present(alert, animated: true, completion: nil)
                }
                resultHandler(nil)
                return
            }

            // JSONで返却されたデータをパースして格納する
            guard let data = data else {
                // データなし
                resultHandler(nil)
                return
            }

            // JSON形式への変換処理
            guard let jsonData = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: Any] else {
                // 変換失敗
                resultHandler(nil)
                return
            }

            // データを解析
            guard let resultSet = jsonData["items"] as? [Any] else {
                // データなし
                resultHandler(nil)
                return
            }
            self.parseData(items: resultSet, resultHandler: resultHandler)
        }
        // 通信開始
        task.resume()
    }
}

最後に、http通信を許可しないと一部の画像が表示されませんので、ATSを無効にします。
Info.plistにApp Transport Security Settingの項目を追加し、Allow Arbitrary LoadsをYESに。
スクリーンショット 2017-02-20 10.32.44.png

完成
スクリーンショット 2017-02-20 10.22.09.png

Google Custom APIの無料版は、1日100クエリまでの制限があるみたいです!!!

20
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
20