Google Search with Alamofire Fails (iOS, Swift, JSON, HTML)

alamofire, google-search, html, json, swift

Solution

You seem to be using the Custom Search API for embedding into web pages.

This documentation is for what you want to do.

Here is an example using stack overflow as the search domain.

import UIKit
import Alamofire

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        search(query: "swift") { (results) in

            for result in results {

                dump(result)
            }
        }
    }

    func search(query:String, completion: @escaping (Array<SearchResult>)->()){

        let id = "Custom search engine ID"

        let key = "API key"

        let url = "https://www.googleapis.com/customsearch/v1?key=\(key)&cx=\(id)&q=\(query)"

        Alamofire.request(url).responseJSON { (response) in

            var results = Array<SearchResult>()

            if let dict = response.value as? Dictionary<String,Any> {

                if let items = dict["items"] as? Array<Dictionary<String,Any>> {

                    for item in items {

                        if let result = SearchResult(dict: item) {

                            results.append(result)

                        } else {

                            print("Incomplete search result data.")
                        }
                    }
                }
            }

            completion(results)
        }
    }
}

I have this struct to store the search results better. It doesn't contain all the values that the JSON results does. I just chose these ones for testing.

struct SearchResult {

    var displayLink: String

    var formattedUrl: String

    var htmlFormattedUrl: String

    var htmlSnippet: String

    var htmlTitle: String

    var link: String

    var snippet: String

    var title: String

    init?(dict:Dictionary<String,Any>) {

        guard

            let displayLink = dict["displayLink"] as? String,

            let formattedUrl = dict["formattedUrl"] as? String,

            let htmlFormattedUrl = dict["htmlFormattedUrl"] as? String,

            let htmlSnippet = dict["htmlSnippet"] as? String,

            let htmlTitle = dict["htmlTitle"] as? String,

            let link = dict["link"] as? String,

            let snippet = dict["snippet"] as? String,

            let title = dict["title"] as? String

        else {

            return nil
        }

        self.displayLink = displayLink

        self.formattedUrl = formattedUrl

        self.htmlFormattedUrl = htmlFormattedUrl

        self.htmlSnippet = htmlSnippet

        self.htmlTitle = htmlTitle

        self.link = link

        self.snippet = snippet

        self.title = title
    }
}

This is a couple of results that `dump(result)` prints out.

▿ CustomGoogleSearch.SearchResult
  - displayLink: "stackoverflow.com"
  - formattedUrl: "https://stackoverflow.com/questions/tagged/swift"
  - htmlFormattedUrl: "https://stackoverflow.com/questions/tagged/<b>swift</b>"
  - htmlSnippet: "<b>Swift</b> is an open-source programming language developed by Apple. Use the tag <br>\nonly for questions about language features, or requiring code in <b>Swift</b>. Use the&nbsp;..."
  - htmlTitle: "Newest &#39;<b>swift</b>&#39; Questions - Stack Overflow"
  - link: "https://stackoverflow.com/questions/tagged/swift"
  - snippet: "Swift is an open-source programming language developed by Apple. Use the tag \nonly for questions about language features, or requiring code in Swift. Use the ..."
  - title: "Newest \'swift\' Questions - Stack Overflow"

▿ CustomGoogleSearch.SearchResult
  - displayLink: "stackoverflow.com"
  - formattedUrl: "stackoverflow.com/documentation/swift/topics"
  - htmlFormattedUrl: "stackoverflow.com/documentation/<b>swift</b>/topics"
  - htmlSnippet: "58 example-focused documentation topics for <b>Swift</b> Language."
  - htmlTitle: "All <b>Swift</b> Language Topics - Stack Overflow"
  - link: "http://stackoverflow.com/documentation/swift/topics"
  - snippet: "58 example-focused documentation topics for Swift Language."
  - title: "All Swift Language Topics - Stack Overflow"

Getting the keys

On this page, click the `Get A Key` button.

You then need to select or create a Google project, then you will be generated an API Key. So copy this an put in here `let key = "API key"`

For the Search Engine ID, go to your console. Now either select a search engine or create a new one. You'll get to a page like this.

Click the `Search engine ID` button, this will display a screen with your id, then put that here `let id = "Custom search engine ID"`

Problem

Please help! I'm stuck for months now trying to perform a simple Google Search on my Swift App and I'm way past smashing my head through the wall! I've tried both with ALAMOFIRE and With a Regular URLRequest, but since the result seems to be only in HTML Format, I Can't seem to parse the results correctly. Even when you look into the HTML Format, the code is for a Webpage, and it does NOT Include the Search Results. I Would LOVE to have the search Results into a Simple Dictionary. Here is my Code: ``` let googleUrl:String = "https://cse.google.com/cse/publicurl?&output=json&cx=<MyGoogleKey>:<MyGoogleSKey>&q=q=+normal+search" // Trying with AlamoFire: Alamofire.request(googleUrl).response { response in print("Request: \(response.request)") print("Response: \(response.response)") print("Error: \(response.error)") }.responseJSON(completionHandler: { response in print("ResponseJSON: \(response)") }).responseData(completionHandler: { response in print("ResponseData: \(response)") }).responseString(completionHandler: { response in print("ResponseString: \(response)") }) ``` As you See I Try the Response in almost ALL Alamofire supported Types and I GEt NOTHING. Here is Error #1 (.responseJSON): The data couldn’t be read because it isn’t in the correct format. ResponseJSON: FAILURE: responseSerializationFailed(Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})) Here is DATA Response (.responseData): ResponseData: SUCCESS: 4337 bytes Here is the Response HTML (.responseString) ResponseString: SUCCESS: *> Google Custom Search (function(){var cookie_path='/cse/';var path_copy='/coop/';window._gaq = window._ga...._AND_SO_ON_TILL_FULL_HTML_PAGE_IN_A_STRING....* * I Only wish I Could have the Search Results in a Simple Dictionary... Anyone? Please?

Original source