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

完成品
スクリーンショット 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クエリまでの制限があるみたいです!!!


『 Swift 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。