Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Creating a Simple iOS REST Client Using HTTP-RPC

DZone's Guide to

Creating a Simple iOS REST Client Using HTTP-RPC

We take a look at how to make a simple REST client on iOS using the HTTP-RPC iOS client library. Come have a look!

· Mobile Zone
Free Resource

Discover how to focus on operators for Reactive Programming and how they are essential to react to data in your application.  Brought to you in partnership with Wakanda

HTTP-RPC is an open-source framework for simplifying development of REST applications. It allows developers to create and access web services using a convenient, RPC-like metaphor while preserving fundamental REST principles such as statelessness and uniform resource access. The project currently includes support for implementing REST services in Java and consuming services in Java, Objective-C/Swift, or JavaScript.

Although it is, of course, possible to use HTTP-RPC’s client libraries to invoke web services implemented using the server-side framework, this isn’t strictly required. The client APIs can actually be used to access any JSON-based REST service, regardless of server platform.

This article provides a demonstration of how the HTTP-RPC iOS client library can be used to invoke services provided by JSONPlaceholder, a “fake online REST API”.

Service API

JSONPlaceholder offers a collection of web services that simulate common REST operations on a variety of resource types such as “albums”, “photos”, and “users”. For example, the following URL retrieves a JSON document containing a list of simulated user records:

https://jsonplaceholder.typicode.com/users

The document is similar to the following:

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  },
  ...
]

Additionally, the service provides a collection of simulated discussion posts, which can be retrieved on a per-user basis as follows:

https://jsonplaceholder.typicode.com/posts?userId=1

For example:

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  ...
]

Sample Application

The sample application presents two views. The first one displays a list of users:

The second displays a list of posts by the selected user:

WSWebServiceProxy Class

The WSWebServiceProxy class is used to invoke service operations. Internally, this class uses an instance of NSURLSession to issue HTTP requests. NSJSONSerialization is used to decode the response data.

Service methods are executed by calling invoke:path:arguments:resultHandler:. This method takes the following arguments:

  • method – the HTTP method to execute (e.g. “GET”, “POST”)
  • path – the resource path
  • arguments – a dictionary containing the request arguments as key/value pairs
  • resultHandler – a callback that will be invoked upon completion of the method

A convenience method is also provided for executing operations that don’t take any arguments.

Arguments are passed to the service either via the query string or in the request body, like an HTML form. Array arguments represent multi-value parameters and are handled similarly to <select multiple> tags in HTML.

The result handler is a callback that is invoked upon completion of the request. If the operation completes successfully, the first argument will contain the result of the operation. If the operation fails, the second argument will be populated with an instance of NSError describing the error that occurred.

For example, the following code might be used to invoke an operation that returns the sum of two numbers, specified by the “a” and “b” arguments. The service would return the value 6 in response:

// Get sum of "a" and "b"
serviceProxy.invoke("GET", path: "/math/sum", arguments: ["a": 2, "b": 4]) {(result, error) in
    // result is 6
}

Application Delegate

An instance of WSWebServiceProxy is created by the application delegate at startup:

private(set) static var serviceProxy: WSWebServiceProxy!

func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
    AppDelegate.serviceProxy = WSWebServiceProxy(session: NSURLSession.sharedSession(), serverURL: NSURL(string: "https://jsonplaceholder.typicode.com")!)

    return true
}

The user and post view controllers discussed below use this proxy to invoke their respective service operations.

UserViewController Class

The UserViewController class is responsible for presenting the list of users returned from /users. Internally, it maintains an array of objects representing the user list. In viewWillAppear:, if the list has not already been loaded, it uses the service proxy to retrieve the user list from the server. An activity indicator is shown while the method is executing, and hidden once the request has completed:

class UserViewController: UITableViewController {
    let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)

    var users: [[String: AnyObject]]! = nil

    ...

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        if (users == nil) {
            tableView.separatorStyle = UITableViewCellSeparatorStyle.None
            activityIndicatorView.startAnimating()

            AppDelegate.serviceProxy.invoke("GET", path: "/users") {(result, error) in
                self.tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine
                self.activityIndicatorView.stopAnimating()

                if (error == nil) {
                    self.users = result as! [[String: AnyObject]]

                    self.tableView.reloadData()
                } else {
                    NSLog(error!.localizedDescription)
                }
            }
        }
    }

    ...
}

A custom cell class is used to present the user details for each row:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return (users == nil) ? 0 : users.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let user = users[indexPath.row]

    let cell = tableView.dequeueReusableCellWithIdentifier(UserCell.self.description()) as! UserCell

    cell.nameLabel.text = user["name"] as? String
    cell.emailLabel.text = user["email"] as? String

    return cell
}

Finally, when a user is selected, an instance of PostViewController is created and pushed onto the navigation stack:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let postViewController = PostViewController()

    postViewController.userID = users[indexPath.row]["id"] as! Int

    navigationController?.pushViewController(postViewController, animated: true)
}

PostViewController Class

The PostViewController is responsible for presenting the list of user posts returned from /posts. Like UserViewController, it maintains an array of object representing the server response, and populates the array in viewWillAppear::

class PostViewController: UITableViewController {
    var userID: Int!

    let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)

    var posts: [[String: AnyObject]]! = nil

    ...

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)

        if (posts == nil) {
            tableView.separatorStyle = UITableViewCellSeparatorStyle.None
            activityIndicatorView.startAnimating()

            AppDelegate.serviceProxy.invoke("GET", path: "/posts", arguments: ["userId": userID]) {(result, error) in
                self.tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine
                self.activityIndicatorView.stopAnimating()

                if (error == nil) {
                    self.posts = result as! [[String: AnyObject]]

                    self.tableView.reloadData()
                } else {
                    NSLog(error!.localizedDescription)
                }
            }
        }
    }

    ...
}

Again, a custom cell class is used to present the details for each row:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return (posts == nil) ? 0 : posts.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let post = posts[indexPath.row]

    let cell = tableView.dequeueReusableCellWithIdentifier(PostCell.self.description()) as! PostCell

    cell.titleLabel.text = post["title"] as? String
    cell.bodyLabel.text = post["body"] as? String

    return cell
}

More Information

This article provided a demonstration of how the HTTP-RPC iOS client library can be used to build a simple REST client application. The complete source code for the sample application can be found here.

The latest version of HTTP-RPC can be downloaded here. For more information, see the project README.

Learn how divergent branches can appear in your repository and how to better understand why they are called “branches".  Brought to you in partnership with Wakanda

Topics:
web services ,ios ,rest ,http-rpc ,mobile

Published at DZone with permission of Greg Brown, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}