본문 바로가기

Apple/iOS7

Create your own web service for an iOS app, part one

Follow the steps in this tutorial to learn how to create the web service backend for an iOS app called iGlobe. 

In my previous TechRepublic series, we used an existing web service to create an iOS app ("Creating a web service, parts one and two"). In this series, we'll create the web service backend for a different app called iGlobe, which is a game I created a while ago.

Basically, we want the user to be able to tag a place or a person and get points for that action. (The competitor with the most points at the end of the game wins a money pot.) In order to do this, the app has to interact with a web service, which we'll create. Our web service will need to be able to:

  • Store each player's username information
  • Receive users' points
  • Present users' points

In this tutorial we'll use a helper class called SantiappsHelper, which contains the code to run these connections to the web. A standalone class in such cases is usually called a library, which takes care of those processes. If you only require one instance of such a class (like in our case), you create a Singleton pattern. You only want one instance of the connection, because you don't want many instances of the connection class creating, receiving, and disconnecting -- that could end up in multiple connections to the same resource at different times, which could confuse you or the server.

Here's what we’ll cover in this three-part series:

1. Create the web database

2. Create the web service backend

3. Create iOS frontend (Storyboard)

4. Fetch data

  • a.  NSURLConnection
  • b.  GCD and completion blocks

5. Add the Bump API

6. Throw in social

Step 1: Create the web database

Web services are usually large databases of information; our database will need a table to store all the information we mentioned above. We interact with databases in four main ways: Create, Read, Update, and Delete (CRUD) data. So let's take a short detour and talk about databases -- specifically, their structure and how we interact with them.

Databases

Databases are information stores, which can be written as files (such as Word or PowerPoint). The information in such files has a predetermined structure that Word and PowerPoint know how to read and access in order to present what you want and let you edit it and store it again; the problem is only Word will read a docx file, and only PowerPoint will read a pptx file. The great advantages of databases are they store information in a very compact way and can be read by many different interfaces. The simpler the database, the more interfaces can read it.

We'll use a database that is usually available for free in most web hosting services. My web hosting service has phpMySQL, which comes included with a free package. If you want other databases such as MySQL, you need a paid service. Figure A is what my database management interface looks like.

Figure A

iGlobeFigA_100113.png

See an enlarged view of Figure A.

We have a database named iglobe on localhost with two tables: users and tags. The users table (Figure B) contains a primary key with a username, a password, a password hint, a first and last name, as well as an email, a phone number, an address, and such regular stuff.

Figure B

iGlobeFigBb_100113.jpg

See an enlarged view of Figure B.

The tags table (Figure C) also has its own primary key (tagID), the corresponding username, an identifier, the tag's latitude and longitude, the date it was created, and how many points it's worth to that user. There is also a country field, which was implemented later as the project progressed (it's been in the works since 2011).

Figure C

iGlobeFigC_100113.png

See an enlarged view of Figure C.

Step 2: Create the web service backend

The idea for our web service will be to read from these tables and write to them whatever data users request or post to them. This part requires you to know some PHP. Let's start by looking at what the code to read a table looks like.

<?php

include_once("JSON.php");
$json = new Services_JSON();

$link = mysql_pconnect("localhost", "user", "pass") or die("Could not connect");
mysql_select_db("iglobe") or die("Could not select database");

$arr = array();

$rs = mysql_query("SELECT * FROM users");
while($obj = mysql_fetch_object($rs)) {
	$arr[] = $obj;
}
 
Echo $json->encode($arr);

?>

First, we include the JSON.php file in order to access JSON files on your server (make sure your webserver or host provides you with at least PHP 5.2). Then, we make a connection to the database using the database user and password as well as the database host. Now we create an array object so once we execute the mysql_query where all entries from the users table are collected into $rs, we can put that object into our $arr[] object. Finally, we encode the $arr into $json and echo it onto the screen.

Once this code is up and ready along with your database (including some records), you can direct your browser to this file (which I called myserver.com/getusers.php).  I get the following result:

[{"id":"35","username":"zlitsami ","password":"932d1c42a4e4880e57037994fd3584b1","password_hint":"","lastname":"","firstname":"","email":"joe@iglobe.com","phone":"","address1":"","address2":"","city":"","state":"","zip":"","country":"","url":"","permissions":"1","udid":"9","userCreated":"2013-01-01 14:27:22","time_queued":null,"time_sent":null}, {another}, {another}]

This is an array that has many elements in it. Each element is a user's table entry. Each entry is a dictionary of key value pairs. Look familiar?

Now that we know how to read information from our database, let's create the code for writing to the database.

<?php
$con = mysql_connect("localhost","user","pass");
if (!$con)
  {
  die('Could not connect: ' . mysql_error());
  }

mysql_select_db("iglobe", $con);

$sql="INSERT INTO  tags (username, latitude, longitude, country,destintyudid,points) VALUES ('$_POST[sender]','$_POST[latitude]','$_POST[longitude]','$_POST[country]','$_POST[receiver]','$_POST[points]')";

if (!mysql_query($sql,$con))
  {
  die('Error: ' . mysql_error());
  }

echo "1 record added to tags";


mysql_close($con)
?>

We connect to our database again, and we create a sql statement with values to insert (these values come from a form that was either online or on a mobile device).  We execute that sql statement with our connection and echo the results for verification to the user. I called this file writephp.php.

Before we move onto iOS, let's test our service online. Create an HTML file called Writeform.html and save this code to it:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<form action="writephp.php" method="post">
  <table style="text-align: left; width: 100%;" border="0"
 cellpadding="2" cellspacing="2">
    <tbody>
      <tr>
        <td>Name:</td>
        <td><input name="sender" type="text"></td>
        <td></td>
      </tr>
      <tr>
        <td>UDID(unnecessary):</td>
        <td><input name="udid" type="text"></td>
        <td></td>
      </tr>
      <tr>
        <td>Latitude:</td>
        <td><input name="latitude" type="text"></td>
        <td></td>
      </tr>
      <tr>
        <td>Longitude:</td>
        <td><input name="longitude" type="text"></td>
        <td></td>
      </tr>
      <tr>
        <td>Country</td>
        <td><input name="country" type="text"></td>
        <td></td>
      </tr>
      <tr>
        <td>Receiver</td>
        <td><input name="receiver" type="text"></td>
        <td></td>
      </tr>
      <tr>
        <td></td>
        <td></td>
        <td><input type="submit"></td>
      </tr>
    </tbody>
  </table>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
</form>
</body>
</html>

Now load the form on your web browser and submit data to your database.

I don't want to make this web service too complicated because I want to keep your attention on the iOS side, so let's create a form to eventually read points from our web service for a particular user. Create another HTML file called Testform.html and save this code to it:

<HTML>
<head>

<form action="readpoints.php" method="post">
User: <input type="text" name="userNa"/>
<input type="submit" />

</form>
</head>
</HTML>

And create its php counterpart:

<?php

include_once("JSON.php");
$json = new Services_JSON();

$link = mysql_pconnect("localhost", "user", "pass") or die("Could not connect");
mysql_select_db("iglobe") or die("Could not select database");

$username = $_POST["userNa"];

$result = mysql_query("SELECT username, SUM(points) AS PUNTOS FROM tags WHERE username='$username' GROUP BY username");
// THIS RETURNS ARRAY NOT READ PROPERLY BY iOS JSON
$resultado = array();
while($obj = mysql_fetch_object($result)) {
	$resultado[] = $obj;
}
Echo $json->encode($resultado);

?>

We'll use this last bit of code later once we get more data into the database.

So far, we have a resource that returns the points for a particular user, readpoints.php; this is what's called a web service endpoint. Web services can have many endpoints. In a game or an app, we might want to get a lot of users' points at once to fill up a leader board, for example. We might want to fetch a lot of transactions from an invoice database instead of one by one. So let's get ahead of ourselves and create an endpoint to manage a set of input data. In our case, we must be able to pass the web service a set of users. Our file would look something like this:

<?php

include_once("JSON.php");
$json = new Services_JSON();

//1. PROCESS RECEIVED ARRAY
$handle = fopen("php://input", "rb");
$http_raw_post_data = '';
while (!feof($handle)) {
    $http_raw_post_data .= fread($handle, 8192);
}
fclose($handle);

//1.1 Just decode to see what kind of object it is
$post_data = json_decode($http_raw_post_data,true);

if (is_array($post_data))
    $response = array("status" => "ok", "code" => 0, "original request" => $post_data);
else
    $response = array("status" => "error", "code" => -1, "original_request" => $post_data);

//2. CALL DB QUERY
$link = mysql_pconnect("localhost", "quique", "sod123") or die("Could not connect");
mysql_select_db("iglobe") or die("Could not select database");

//3. CREATE FINAL ARRAY TO RETURN
$arrayToReturn = array();

//4. CYCLE THROUGH USERS
foreach ($post_data as $value) 
{
  //CREATE QUERY
  $result = mysql_query("SELECT username, SUM(points) AS PUNTOS FROM tags WHERE username='$value' GROUP BY username");

  //EXECUTE QUERY & ADD EACH USER/POINTS DICTIONARY TO $resultado ARRAY
  $resultado = array();
  while($obj = mysql_fetch_object($result)){
	$arrayToReturn[] = $obj;
  }
}
Echo $json->encode($arrayToReturn);
?>

This basic php code takes the passed in array as we mentioned and loops through the database to get the points for each user. This is important because we save the app a lot of trips to the web server database.

Step 3: Create iOS frontend (Storyboard)

We'll now work on our iOS Storyboard or frontend. Then we'll hardcode data and fetch web from the actual backend; this way, we can see what our frontend will require in terms of data models, and then we can fetch web data and replace our data in those data models. We'll also be learning two ways of fetching data: inline, messy code and neat and tidy coding.

Follow these steps:

  1. Create a new Empty project using Storyboards, ARC, iPhone, and NO Core Data.
  2. Go to the storyboard and drag a UITableViewController onto the grid.
  3. Create a class called UsersListViewController. In Storyboard, select the scene and in the Identity Inspector make our scene UsersListViewController type from the dropdown list.
  4. Run a quick test to make sure our tvc is working.
  5. Build & Run. You should get an empty tableview.

Let's review what we'll do in this section:

  • Add an array property to your .m file
  • Prefill that array in viewDidLoad
  • Eliminate the pesky warning lines
  • Make tableview return one section
  • Make tableview return array count
  • Make tableview cell return array objects

This should be second nature to you by now, so I'll blaze through the specifics. 

Here's the property code:

@property (nonatomic, strong) NSArray *testArray;

Here's the viewDidLoad code:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.testArray = [[NSArray alloc] initWithObjects:@"me", @"you", @"them", nil];
    NSLog(@"array %d", [self.testArray count]);
}

Here's the return array count code:

return [self.testArray count];

Here's the cFRAIP code:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
    // Configure the cell...
    cell.textLabel.text = [self.testArray objectAtIndex:indexPath.row];
    return cell;
}

Before you Build & Run, select the UITableViewCell in Storyboard and, in the Attributes Inspector, make sure you use Cell as the Reuse Identifier. Your app should work fine.

If you Build & Run now, users should be displayed in the tableview. Cool!  That's what we're going to want to do -- that is, display a list of users in a tableview and then add in the points, like a score table.

Figure D is a mockup of what our app will look like. In essence, we'll have a tab bar controller manage three views: Users, Map, and Instructions. We'll also throw in a Login view as the app launches. This should give you an idea of what kinds of tasks we'll need to perform in order to accomplish this.

a)  Present a login view controller

b)  Save user and pass information

c)  Fetch user data from the web service

d)  Plot points on a map

e)  Display instructions in a view

Figure D

iGlobeFigD_100313.png

See an enlarged view of Figure D.

You should be able to re-create this in your Storyboard. Here are the basic steps:

1. Select your existing UsersViewController scene and, from the Editor menu, select Embed In | Tab Bar Controller. You should have a scene and a class for UsersViewController, and the scene should be set to its Class Type in the Identity Inspector.

2. Clear out the second scene that was added when you embedded your tableview scene in a tab bar (by clear out, I mean make sure it doesn’t have any labels or other controls in it). Now drag a UIMapView into it. Add a UINavigationBar to the top and two buttons (Plot and Bump) on either side. Create a MapViewController class for it and set its type. Add a MKMapView IBOutlet property and two UIBarButtonItem IBOutlet properties and connect them. Add the MKMapViewDelegate.

3. For the last view add another UIViewController and drag a UIWebView and a UINavigationBar into it. Create its class file and name it InstructionsVC. Add a UIWebView IBOutlet property and connect it. Add the UIWebViewDelegate and don’t forget to set its scene type.

4. Add a UIViewController, call it ModalViewController (Figure E is what mine looks like), and create all of the IBOutlet properties for it -- that's four labels with static text (User, Pass, Email, and Pass Requires…). There are three UITextFields with placeholder text to guide the user. There are three UIButtons for different actions. The person icon is a button with a Background Image set to the image; it will be the button the users will use to upload their image to the web server.

Other class files we could create now are TagListController, Tag/Users Model, and Annotation/PlacemarkVC.

Figure E

iGlobeFigE_100113.png

See an enlarged view of Figure E.

Take a couple of minutes to visualize what the app layout will look like now that we have a better idea of where we're headed, and then compare your visualization to the initial sketch you made of your app.

In part two, we'll start the iOS app with basic read write functionality. In part three, we'll add Facebook, Bump, and other cool stuff.