Scriptico

HP IDOL OnDemand. Query Text Index Swift Example

HP IDOL OnDemand provides format conversion, image analysis, indexing, and text analysis RESTful APIs to help the developers process unstructured data and build amazing, innovative, flexible, and robust solutions.

The following article demonstrates how to consume one of the available HP IDOL OnDemand APIs and query a text index from an application written with Swift, a new programming language for iOS and OS X. Let’s start from the basics.

Background

Firstly, we need to clarify what the Query Text Index API is about by considering the following example.

Let’s assume, your solution is used by large companies, and it consists of mobile and desktop versions. The company workflow produces a number of documents and employees are using the mobile version of your application to search over the documents and check results.

As a developer, you do not really want to invest a number of days to develop backend solutions to keep the documents, index them, and make a custom search engine. You want to make your application powerful, stable and flexible as well as reduce development expenses and save the project budget.

With HP IDOL OnDemand you can save and index documents and provide the content search feature. The Query Text Index API allows you to use natural language text as a query as well as document keywords and boolean expressions and, if any document matches the query, it returns the resulted documents.

Now, when we know a bit more about the API, let’s define goals for this article.

Goals

For now, let’s specify a simple goal – our application has to be able to query text index by using the RESTful API and output the returned result. As a default query value, we will use ‘Hewlett Packard’, decode the API response, and print out the decoded JSON result.

Requirements

XCode

As I stated above, for this example, I am using the Swift language and freshly introduced Xcode playgrounds. Thus, you should have installed Xcode locally. To write the example application, I used Xcode 6.1 Beta 2 and there is a chance that the project, available on GitHub, will fail with a different version of Xcode since the Swift language is in beta for now.

HP IDOL OnDemand Account

The second requirement is the developer account and its API key. In order to run the application successfully, you should create a new HP IDOL OnDemand developer account and then use the given key.

To create a new account, please proceed to https://www.idolondemand.com/signup.html and after the successful registration, sign in and navigate to the https://www.idolondemand.com/account/api-keys.html page to generate a new key. The service is free for now.

Eventually, you should see your keys as shown below

HP IDOL OnDeman API Keys

Implementation

Xcode Project

Luckily, we do not need to create a complex project with a number of settings. We are going to use Playgrounds! The feature has been recently introduced by Apple and it allows developers to edit the code listings and see the result immediately!

To make a new playground, just open Xcode and in the welcome screen click on the “Get started with a playground” button. On the next screen, provide the playground name, select iOS as a platform and pick a destination to save the playground! If the default screen is disabled in your Xcode, please proceed to the File -> New -> Playground… menu item.

The created playground does not require any additional configuration; we are ready to go!

HP IDOL OnDemand RESTful Client

In mobile applications, UI works in the main thread and if something blocks the thread for too long, UI is lagging and freezing. If we made a sync client, any calls from UI to the client (e.g. a user click on the button to run the query) could block the main thread during the execution.  Therefore, our client has to work asynchronously.

The basic idea of the async request is to avoid the waiting time during the request; instead, a component adds notification observers to the notification center and then invoke the client method to perform the call. The client does not hold the main thread until the response is received, yet releases the thread immediately after the call and later, when the response is received,  posts the specified notification along with the received result to the notification center. The notification center, in its turn, notifies all registered observers about the received notification. The component, which added an observer before the call, receives a call to the notification handler (i.e. function) and handles the received data. Everything described above is going in the other thread and does not block UI. As soon as the result is received, the component performs UI updates in the main thread. You can read more about notifications by checking this article in the iOS Developer Library.

For now, we are done with theory; let’s open the playground and write some code!

//
//  DefaultPlayground.playground

import UIKit
import XCPlayground

XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)

//

As you can notice, in addition to UIKit, I imported XCPlayground module. By adding the highlighted line of code, we allow to continue the code execution after the end of top-level code and as a result, receive async responses from API.

Now, let’s define four constants to specify the API protocol, url, path, and key as shown below:

//
//  DefaultPlayground.playground

import UIKit
import XCPlayground

import UIKit
import XCPlayground

XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)

let API_KEY = ""

let API_PROTOCOL = "https://"
let API_HOST = "api.idolondemand.com"
let API_PATH = API_PROTOCOL + API_HOST + "/1/api/sync/querytextindex/v1text=%@:title&indexes=wiki_eng&apikey=" + API_KEY

//

As you can see, I left the API_KEY constant empty, so please do not forget to provide your key to make it work. Now, let’s define our client class below the defined constants:

//
//  DefaultPlayground.playground


class HPIDOLPlaygroundClient {
     
}

//

In this application, we are going to communicate with API over a secure connection and our client has to respond to authentication challenges and set appropriate credentials for the received challenge. To make it done, we have to create an NSURLConnection delegate which will extend the NSURLConnectionDataDelegate protocol. This protocol defines a number of functions, and to handle the case described above the connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) method is required (see the next code snippet). In addition to this method, our delegate will implement connection(connection: NSURLConnection, didReceiveData data: NSData) and  connection(connection: NSURLConnection, didFailWithError error: NSError) as described below.

To keep things simple, our client will be the NSURLConnectionDataDelegate itself and to do so, let’s extend the protocol and implement the required functions as the following:

//
//  DefaultPlayground.playground

class HPIDOLPlaygroundClient : NSObject, NSURLConnectionDataDelegate {
    
    func connection(connection: NSURLConnection, didReceiveData data: NSData) {
        //
    }
    
    func connection(connection: NSURLConnection, didFailWithError error: NSError) {
        // debugging the error description
        println(error.description)
    }
    
    func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
        if API_HOST == challenge.protectionSpace.host {
            challenge.sender.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust), forAuthenticationChallenge: challenge)
        }
    }
}

//

connection(connection: NSURLConnection, didReceiveData data: NSData) is called when the data is received and connection(connection: NSURLConnection, didFailWithError error: NSError) will be called if an error occurs during the loading. As you can see, we are just printing the error description if something went wrong. Now, let’s implement the data handle method to decode the received data as JSON and print it out.

//
//
func connection(connection: NSURLConnection, didReceiveData data: NSData) {
    var error: NSError?
    var jsonObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error)
    if nil == error {
        // printing out the result
        println(jsonObject!)
    } else {
        println("Unable to serialize received data to json object: " + error!.description)
    }
}
//

Now, when all required methods are implemented, we are ready to create a custom function to make an actual call to API.

//
//
func queryTextIndex(query: String) {
    var filteredQuery = query.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil)
        
    var urlString = String(format: API_PATH, filteredQuery)
    var url = NSURL(string: urlString)!
        
    if nil != url {
        var request = NSURLRequest(URL: url!)
        NSURLConnection(request: request, delegate: self, startImmediately: true)
    } else {
        println("url is malformed")
    }
}
//

That’s easy! At the fist step, we should replace the query spaces by the plus char since we will add it as a parameter value to our url. Then we are formatting our url to call properly (i.e. setting the query string as a value for the text parameter) Next, we are creating NSURL object with the formatted string and an instance of NSURLRequest. The very last step is to create an instance of NSURLConnection and provide the created request and a delegate object (i.e. a reference to the client itself) to it. In the implementation, we start to begin the load since no other configuration is required.

Now, our basic client is ready and we can test it. To do so, you should create a new instance of the client and invoke queryTextIndex(query: String) after the client class

HPIDOLPlaygroundClient().queryTextIndex("Hewlett Packard")

To see the execution result, you should show the assistant editor in Xcode (View -> Assistant Editor -> Show Assistant Editor). I got the following:

HP IDOL OnDeman query text input result output

And which result did you get? Just for any case, let me show the complete playground below

//
//  DefaultPlayground.playground
//
import UIKit
import XCPlayground

XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true)

let API_KEY = ""

let API_PROTOCOL = "https://"
let API_HOST = "api.idolondemand.com"
let API_PATH = API_PROTOCOL + API_HOST + "/1/api/sync/querytextindex/v1?text=%@:title&indexes=wiki_eng&apikey=" + API_KEY

class HPIDOLPlaygroundClient : NSObject, NSURLConnectionDataDelegate {
    
    func queryTextIndex(query: String) {
        var filteredQuery = query.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil)
        var urlString = String(format: API_PATH, filteredQuery)
        var url = NSURL(string: urlString)!
        
        if nil != url {
            var request = NSURLRequest(URL: url!)
            NSURLConnection(request: request, delegate: self, startImmediately: true)
        } else {
            println("url is malformed")
        }
    }
    
    func connection(connection: NSURLConnection, didReceiveData data: NSData) {
        var error: NSError?
        var jsonObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error)
        if nil == error {
            // printing out the result
            println(jsonObject!)
        } else {
            println("Unable to serialize received data to json object: " + error!.description)
        }
    }
    
    func connection(connection: NSURLConnection, didFailWithError error: NSError) {
        // debugging the error description
        println(error.description)
    }
    
    func connection(connection: NSURLConnection, willSendRequestForAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
        if API_HOST == challenge.protectionSpace.host {
            challenge.sender.useCredential(NSURLCredential(forTrust: challenge.protectionSpace.serverTrust), forAuthenticationChallenge: challenge)
        }
    }
}
HPIDOLPlaygroundClient().queryTextIndex("Hewlett Packard")
//

As you can note, the playground outputs the created objects immediately if the code is OK or shows an error message in the faulty line. It saves a lot of time and makes the development process fun! No endless builds and runs, just write the code and check the result! Now it is time for a fly in the ointment.

Simple Playground UI

Although playgrounds support UIKit and developers can make views with controls, I was not able to make UI interactive. After some research, I’ve found that the root of the problem is not the interactive Xcode playgrounds and for now, neither a solution nor a workaround exists.

I published my attempt to make UI in the playground to GitHub  and if you will, you can take a look at it as well (UIExamplePlayground) and if you know how to make it work, please write a line in comments.

Screencast

My blog editor shows 1548 words at this point and fingers are begging for mercy. Your example works as well as mine, and we did a great job together! Now, you can check the video in addition to the article and play with either your project or mine, available on GitHub. Practice makes it perfect, doesn’t it?

References

That is it! In case of questions, feel free to write a comment below and if you are exited about the topic and ready to slightly embellish our example, please check my other article HP IDOL OnDemand. Query Text Index Example with UI

Category: Development, HP IDOL OnDemand, iOS, iOS, Swift

Tagged: , , ,

One Response

  1. […] the Query Text Index Swift Example article, I demonstrated how to query text indexes in IDOL OnDemand. A simple RESTful client was […]

Leave a Reply

ERROR: si-captcha.php plugin: GD image support not detected in PHP!

Contact your web host and ask them to enable GD image support for PHP.

ERROR: si-captcha.php plugin: imagepng function not detected in PHP!

Contact your web host and ask them to enable imagepng for PHP.