Accessing The Cloud From Cocoa Touch
Everything is moving toward the cloud and unless you’re building calculators, unit converters, or miniature golf score keepers your iPhone app needs to know how to get data from it. In this blog post I intend to demonstrate how to set up a simple server application and how to retrieve data from it and post data to it using Cocoa Touch. I have chosen to use PHP on the server side because of it’s simplicity and ubiquity, and because I’ve know it, somewhat. You should, however, be able to implement something similar using your server side language of choice.
In many cases when you go to access remote data, you do so through a web service API. While services based on such technologies as SOAP or XML-RPC are standards that provide reasonable methods for retrieving and updating data, REST seems to be the methodology gaining the most ground lately. For our purpose in this post I won’t get into great detail of how to implement a REST base web service as, again, REST is not a specific implementation but rather a methodology. (Read up on it elsewhere if you don’t understand what this means). However, I will talk about it briefly so that you can get on the right path for doing your own REST implementation.
What Is The Cloud
It seems that the term ‘cloud’ in this context has been around for a pretty long time, however, you can just think of it as, well, the Internet. If you access your data “in the cloud”, you are accessing your data that is hosted on some server somewhere in the world. The idea behind it being that you can always access it no matter where you are. If your data is not “in the cloud”, then it is hosted locally only and only accessible from that location.
Amazon.com among other technology leaders has helped to make this metaphor easier to understand. If you are familiar with Amazon S3 (Simple Storage Service), then you know what it means to store your data “in the cloud”. Amazon has made it very cheap and very easy to access data that you store on their servers from anywhere in the world. They provide a RESTful web service through which you can securely add, remove, and update data that you have stored there. If you have image or video assets, for example, that you find get accessed a lot through your website, you may find that hosting those files on S3 is cheaper than paying your own web host to provide the bandwidth and storage for them. This is a perfect example of what cloud computing is and provides.
On a more generic level, cloud computing can mean setting up your own service and providing access to resources or data in the same fashion. The difference in this case, however, is that you are managing it all on your own. Let’s take a look at how you might do this.
Sending Arbitrary Data
The simplest PHP script can really teach you a great deal about what is going on between client and server. Remember that since we are depending on the Apache web server to serve up our PHP responses a huge portion of the work is already done. We don’t have to worry about low-level networking APIs and sockets. Instead we can create a simple connection using a URL and the NSURLConnection class and we’re most of the way there. Consider the following PHP code sample.
With one line of code (not including the tags), we have just implemented an echo server. Whatever you send to this script on the server will be sent right back to you. You should understand though that it is the body of the request that gets stored in this $HTTP_RAW_POST_DATA variable. So what does that mean?
This really means that you can send anything you want as the body of the request and this script will store it in the $HTTP_RAW_POST_DATA and then print it back as the response. You just specify in your request the type of data you’re sending. Say you want to send raw XML, for example, you specify”‘text/xml” as the content-type of the request that you will hand off to your NSURLConnection as in the following code snippet.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:[NSURL
URLWithString:@"https://www.cimgf.com/testpost.php"]];
[request setHTTPMethod:@"POST"];
[request setValue:@"text/xml"
forHTTPHeaderField:@"Content-type"];
NSString *xmlString = @"- Item 1
- Item 2
";
[request setValue:[NSString stringWithFormat:@"%d",
[xmlString length]]
forHTTPHeaderField:@"Content-length"];
[request setHTTPBody:[xmlString
dataUsingEncoding:NSUTF8StringEncoding]];
[[NSURLConnection alloc]
initWithRequest:request
delegate:self];
When this request finishes, the same XML in the xmlString variable will be sent right back to our application and will be available in our delegate method, – (void)connectionDidFinishLoading:(NSURLConnection *)connection; assuming we’ve been appending the data to an NSMutableData object in our delegate method, – (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)d;.
This example is just echoing back whatever we send, but if we wanted to get a little fancier, we could load and parse the XML with the PHP XML parser and respond back to the client with something more useful.
What is also interesting about this code is that you can replace the body of the request with any data type you’re interested in POSTing to your server. If you post image data, for example, you can save that image data on the server side using something like this PHP script:
Keep in mind that this is very primitive and it does no sanity checking on the body data. You would need to add that in order to implement any real world server side application. That being said, these few lines of code demonstrate how you can send most any data as the request body and process it on the server side. Our Objective-C code from earlier modified to support sending a PNG image would look like this:
NSData *imageData = UIImagePNGRepresentation([UIImage imageNamed@"localimage.png"]);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:[NSURL
URLWithString:@"https://www.cimgf.com/testpostimage.php"]]; // Not a real URL.
[request setHTTPMethod:@"POST"];
[request setValue:@"image/png"
forHTTPHeaderField:@"Content-type"];
[request setValue:[NSString stringWithFormat:@"%d",
[imageData length]]
forHTTPHeaderField:@"Content-length"];
[request setHTTPBody:imageData];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
The request we are sending will be asynchronous so our UI will not hang, however, there is no accurate progress monitoring capability, so you would need to implement that if you want to have an idea of how long it is going to take to post the image up to the web service. See the section called Simplifying Cloud Access below to see one solution to providing progress.
Working With Web Forms
A lot of web applications began life as web forms in which the user can obtain a list of records or a detail record based on input the user provides. You can post form data programmatically using a fairly trivial implementation not much different from the code we demonstrated above. In the previous section we discussed that you can send any arbitrary data to a web service or script by placing that data in the body of the request. This is also true for sending form data.
If you send form data, which is the default when you create an NSURLConnection object and use it to talk to your server, you will see a string of key value pairs in the same format you would normally see in a get request–something like key1=value1&key2=value2&key3=value3, etc. This is what gets sent in the body for web form requests.
Consider the following Bug Reporter web form.
Yes, yes, I know–it’s beautiful. Looks like something you would see circa 1995. Stick with me here as I’m trying to keep things simple. The form takes two fields and just formats the input and responds to the user with what the user entered. This same form can also be submitted using code similar to what we’ve already shown. Here is how you would programmatically post data to this form using and NSURLConnection/NSURLRequest.
NSMutableURLRequest *request =
[[NSMutableURLRequest alloc] initWithURL:
[NSURL URLWithString:@"https://www.cimgf.com/test/testform.php"]];
[request setHTTPMethod:@"POST"];
NSString *postString = @"go=1&name=Bad%20Bad%20Bug&description=This%20bug%20is%20really%20really%20super%20bad.";
[request setValue:[NSString
stringWithFormat:@"%d", [postString length]]
forHTTPHeaderField:@"Content-length"];
[request setHTTPBody:[postString
dataUsingEncoding:NSUTF8StringEncoding]];
[[NSURLConnection alloc]
initWithRequest:request delegate:self];
Notice that we are passing a variable called go in the postString variable. This tells our PHP script to see the request as if the submit button was clicked. You’ll notice in the PHP script that we are checking whether the submit button was clicked with the call to isset($_POST[‘go’]). Take a look at the complete PHP web form script.
Bug Reporter
Fancy Bug Reporter
When the request finishes posting to this form, you will have the same HTML code in your data object as what you would see if you were to view the source in the actual web page after posting the form.
Simplifying Cloud Access
Ben Copsey developed a networking library called ASIHTTPRequest that is a really good replacement for NSURLConnection and related classes. There are a couple reasons I prefer it to NSURLConnection. These include:
- You can specify a delegate selector when you create your request object that will get called back when the request fails or succeeds. This is really just a preference over NSURLConnection/NSURLRequest as it just seems simpler to me.
- If you do want to see accurate progress for either downloads or uploads, you can pass in a reference to a UIActivityIndicatorView that will be updated automatically
- Different types of classes are available for different types of requests. For example, to post form data to a web form, you use ASIFormDataRequest instead of the more generic ASIHTTPRequest. It handles setting up the request body correctly and even enables you to post a file to the form if the form accepts one.
- While Ben still considers it experimental code, there is also support for access to Amazon’s S3. You create an ASIS3Request object and provide your S3 credentials to the request. Then you can create, update, or delete any assets you may be storing in your S3 buckets.
Using the example I mentioned earlier of sending XML to our PHP echo script, the request would now look like the following using the ASIHTTPRequest object:
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
NSString *xmlString = @"- Item 1
- Item 2
";
[request appendPostData:[xmlString dataUsingEncoding:NSUTF8StringEncoding]];
[request setDelegate:self];
[request setDidFinishSelector:@selector(requestFinished:)];
[request setDidFailSelector:@selector(requestFailed:)];
[request startAsynchronous];
Then you implement your delegate selector as in the following:
- (void) requestFinished:(ASIHTTPRequest *)request
{
NSString *response = [request responseString];
// response contains the data returned from the server.
}
- (void) requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
// Do something with the error.
}
Similarly, if you want to submit data to a form like we did earlier using the NSURLConnection/NSURLRequest combination, we can instead use the ASIFormDataRequest class.
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:@"1" forKey:@"go"];
[request setPostValue:@"Bad%20Bad%20Bug" forKey:@"name"];
[request setPostValue:@"This%20bug%20is%20really%20really%20super%20bad." forKey:@"description"];
[request setDidFinishSelector:@selector(requestFinished:)];
[request setDidFailSelector:@selector(requestFailed:)];
[request startAsynchronous];
When this request completes, we will have the formatted HTML in the responseString of the ASIHTTPRequest object:
- (void) requestFinished:(ASIHTTPRequest *)request
{
NSString *response = [request responseString];
// response contains the HTML response from the form.
}
I have come to prefer using ASIHTTPRequest for network access, however, this is a matter of taste. It is a very clean and well written library of classes, so I highly recommend it, but your mileage may vary.
So What About REST?
As I said at the beginning, I’m not going to go into a lot of detail about how to set up a REST web service as implementation of the REST philosophy is really up to the developer. However, here are a few points about how I go about it:
- Create individual server side scripts for each of the functions you want to implement. Instead of creating one master script, create one for each function you want to implement. This helps you maintain the code as when you need to update or fix something, it can be very targted.
- Make heavy use of mod_rewrite in your Apache server. Just use Apache. If you’re using something else, I’m sorry but you’re on your own. The rewrite mechanism that mod_rewrite provides enables you to take difficult to read URLs and make them easy to understand. WordPress, which we use for this blog, changes a URL that looks like this https://www.cimgf.com/?p=235, into something that looks like this: https://www.cimgf.com/2010/01/28/fun-with-uibuttons-and-core-animation-layers/ using mod_rewrite .
It also so happens to provide a great way to implement a REST web service. Say you want to look up a bug by it’s unique ID in a bug tracker database. Your request would normally look like this https://www.cimgf.com/showbug.php?id=1234. The mod_rewrite module allows you to define rewrite rules that would change this URL to https://www.cimgf.com/bugs/1234. The rule to do this is very simple.
RewriteEngine On RewriteRule ^bugs/([[:alnum:]]+)$ /showbug.php?id=$1
The RewriteRule line uses a regular expression to define how to translate from the uglier URL to the pretty one. This regex means the following: “look for the word bugs at the beginning of the parameters string, followed by a forward slash, followed by one or more numbers until you reach the end of the line.” Notice the $1 in the second section. If you are not familiar with perl compatible regular expressions, this simply means use whatever was found between the parens in the regex. In our case the parens are “capturing” the ID of the bug the user is requesting. The rewrite rule is then passing that along to the real showbug.php script.
You just add this to the .htaccess file in the directory where you are hosting your server side scripts and you will be able to load the load your data using the REST looking URL–assuming of course your Apache web server has mod_rewrite enabled. Even if you are using shared hosting, there is no excuse for a web host not to have this enabled. If it is not enabled, you need to find a new web host.
- Drive your web service with a scripted backend that connects to a commodity database like MySQL. Sanity check all of the input you get from your user by looking for vulnerabilities such as SQL injection attacks, but then insert the data into the database using standard SQL calls. If you receive the data, as XML for example, you can simply parse the XML into a DOM object and insert it from there. Better yet, use a (PHP) server side module that converts from XML to SQL for you.
- Use a framework like Rails. Most of the folks I know who have used it have nothing to say but good about implementing web services using Ruby on Rails. I can’t recommend it personally, but people I respect swear by it. It handles a lot of the heavy lifting of developing a web service by constructing the underlying functionality for you after you have specified basic meta data that defines the entities you want to manage.
The down side of this option for me is that I need to learn yet another programming language, Ruby. While I’m getting around to it, it hasn’t been on the top of my priority list.
- Use S3 for Assets. This is simply a suggestion, however, I think it’s a good one. If all you need is to access assets from your iPhone app, keep a structured data document around, like an XML document in your S3 bucket. Let it map out where the assets are stored in your S3 buckets. Then all you have to do is maintain that one XML document and upload a new one each time your content needs to change. It will define for your app where the various assets are located along with any other information you might want to provide about that asset.
Conclusion
Networking code has gotten much simpler to implement in recent years, so accessing your resources in the cloud is a great solution to many application development problems. There are some great tools out there these days regardless of whether you are developing the client or the server. If you need to access the cloud, use the various libraries available to you and keep your designs simple. Doing so will yield great results.
If you have some suggestions for how to implement REST web services for iPhone apps, leave them in the comments section below. Remember that all comments are moderated, so if you don’t see your comment appear right away, just be patient. We’ll approve it as soon as possible. Until next time.
[…] a manejar este entorno para comunicarme con vosotros, aprovecho mi primer artículo para referir a este otro que acabo de encontrar (está en inglés). En él, su autor nos hace el trabajo sucio y explica […]
If you’re more familiar with python, an excellent framework for doing REST is Django (http://www.djangoproject.com/) with Piston (http://bitbucket.org/jespern/django-piston/wiki/Home).
It’s far more complex than your excellent PHP examples above, but I thought it was worth mentioning.
Sort of a side note since this is primarily about the Cocoa Touch, but it is better to use php://input to read the raw post data. $HTTP_RAW_POST_DATA is not always set.
$rawPostData = file_get_contents(“php://input”);
http://php.net/manual/en/wrappers.php.php
@Ryan. No, that’s helpful. As I said, I know PHP somewhat. I appreciate the feedback. I’ll explore php://input a little closer. This is the first I’ve heard of it. ;-)
[…] The tutorial is from Core Animation book co-writer Matt Long, and can be found here: Accessing The Cloud From Cocoa Touch […]
Hi Thanks Matt,
I’m doing this right now but in my case I’m using Ruby on Rails for the REST service. Up to now is going quite well. They are other libraries like httpriot that I have head about but as well as you I simply prefer NSURLConnection.
Hi, Thank you for your blog about this.
I have a question, is it possible to provide more information about your S3 XML doc suggestion. I am a little confused with the direction how to implement this.
What about the inside of the xml, shared docs and security?
Maybe worth a blog posting :-)
Thanks
Thx for this usefull tutorial.
I suggest CakePHP as good framework for the serverside app. It’s based on php, use MVC pattern and do all the hard work of dealing with the database.
http://cakephp.org/
Thank you for this great article. I am a Sun Cert. Java Architect making the venture int IPhone/Andriod Development. Is there a Object Relational Framework for Objective-C for mapping of XML into Objective-C Classes? Something like an JaxB, or XML Objects for this platform (XML-Schema/WSDL Marshalling/unmarshalling frameworks)? Or and SDO (Service Data Objects) implementation?
@mcclayac
In Objective-C we often deserialize XML data into arrays of dictionaries or dictionaries of dictionaries and access what we need by key path instead of creating object hierarchies. There probably are tools to do what you are looking for, but I’ve never used them or had the need to. Depending on what you are doing you may also want to look into use Core Data. I’m really not sure other than that. What have web searches turned up?
-Matt
[…] Accessing The Cloud From Cocoa Touch […]
[…] iOS Data Backup & Syncing Solutions How To Choose The Best XML Parser for Your iPhone Project Accessing The Cloud From Cocoa Touch introduce access web service using asihttprequest and nsurlconnection,list reasons to prefer using […]
[…] http://www.cimgf.com/2010/02/12/accessing-the-cloud-from-cocoa-touch/#more-909 […]