Kotan Code 枯淡コード

In search of simple, elegant code

Menu Close

Backing Your iOS App With WCF JSON Web Services

There is very little doubt that iOS is one of the most compelling targets for developers who want to build free or paid applications for mobile devices. The iOS SDK is, in my opinion, one of the best designed SDKs available. It is better, in fact, than many desktop and server SDKs that I’ve used. Unfortunately, Apple doesn’t have a server-side SDK so when it comes to building web services that support our connected mobile applications we’re left to look elsewhere.

Say what you want about Microsoft and their Windows Phone 7 SDK, but few people doubt that the .NET Framework is on the short list of go-to technologies for enterprise and server-side code. Unfortunately, not enough people know that it is possible to quickly and easily build cross-platform, mobile-friendly web services using Microsoft technologies. Partially their own fault for some of their early work with Web Services, Microsoft has a stigma of being proprietary and that none of their stuff server stuff is easy to consume from non-.NET clients.

This blog post will show you otherwise. For demo purposes I will use a subject that is near and dear to my heart, Zombies. This application is a zombie catalog that pulls a list of known zombie types from a catalog server and displays them in a table view (via a UITableViewController).

First, let’s build the service. To start, we’re going to build a simple C# object that represents the summary information we’re going to pull down to populate our table view:


[DataContract]
public class ZombieInfo
{
 [DataMember]
 public string Name { get; set; }

 [DataMember]
 public string Description { get; set; }

 [DataMember]
 public int TotalSightings { get; set; }
}

Now that we’ve got the data contract for the ZombieInfo class, let’s create a WCF service contract for the service:


[ServiceContract(Namespace="http://my.app.com/services")]
public interface IZombieInfoService
{
 [OperationContract]
 [WebGet(BodyStyle = WebMessageBodyStyle.Bare,
 RequestFormat=WebMessageFormat.Json,
 ResponseFormat=WebMessageFormat.Json,
 UriTemplate="/KnownZombies.json")]
 ZombieInfo[] GetKnownZombies();
}

I won’t bore you with the implementation of the service itself … for the purposes of this demo I just created a few instances of some zombies and returned them. You can choose to host the implementation of this service in IIS or you can self-host it using code like this:


Uri jsonbaseAddress = new Uri("http://" + Environment.MachineName + ":1111/ZombieService");
jsonServiceHost = new ServiceHost(singleton, jsonbaseAddress);
ServiceEndpoint jsonEndpoint = jsonServiceHost.AddServiceEndpoint(typeof(IZombieService), new WebHttpBinding(), jsonbaseAddress);
jsonEndpoint.Behaviors.Add(new WebHttpBehavior());
(jsonEndpoint.Binding as WebHttpBinding).Security.Mode = WebHttpSecurityMode.None;
jsonServiceHost.Open();

In the preceding code, the singleton variable is just an instance of my ZombieInfoServiceImpl class (a class that implements IZombieService).

Now that you’ve got this service running and self-hosted (or running in IIS – however you want to host your WCF services is up to you, but you’ll probably need IIS if you’re hosting them on the Internet somewhere) you can consume it. If you hit the /KnownZombies.json URL and you’ve provided some bogus data, then you should get a blob of information in typical JSON format. In our case, it’s a JSON array (this fact will be important when we get to the iOS code).

Now we can get started with our iPad/iPhone app. I’m not going to cover how to make this a Universal app because I did that in my previous blog post. The first thing you’re going to need is a UITableViewController. It doesn’t make any difference if you rig it through the designer (you are using Xcode 4 right??) or if you do it all programmatically. The key is that we’re going to hit our web service in the viewDidLoad: method.


- (void)viewDidLoad
{
 [super viewDidLoad];

 NSString *urlString = @"http://my.app.com/services/KnownZombies.json";
 NSURL *url = [NSURL URLWirthString:urlString];
 NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
 NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
 [connection release];
 [request release];
}

Now we need to grab the data from the web service, which is going to come to us in the form of an NSData pointer. We’ll need to convert that to a string and then from that string, we need to parse the JSON and turn it into an Objective-C object. To do that, we’re going to use this JSON framework for iOS. I didn’t mess with trying to link the library, I just copied the code into a JSON folder and at the top of my view controller’s implementation file I added an import for “JSON.h”

Assuming we don’t get any errors, then our NSURLConnection delegate method is going to be called. This is where we use the JSONValue category extension provided by the JSON framework to give us an NSArray* object from the string:


- (void)connection:(NSURLConnection *)connection didReceiveData:(NSDAta *)data
{
 NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
 self.knownZombies = [jsonString JSONValue];

 NSLog(@"got zombies! : %@", self.knownZombies);

 [self.tableView reloadData];
}

In this little bit of code, my table view controller has a property on it called knownZombies. In a bigger application I might have gone with a repository pattern to separate the view controller from the actual web service calls and the data storage and so in my viewDidLoad: method I might have just done something like this instead:


[repository fetchZombies];

And then the rest of my table view controller pulls things like rowcount and data from the repository. The last thing we need to do is supply row counts and cells for the table view:


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
 return [self.knownZombies count];
}

- (UITableViewCell *)tableview:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
 static NSString *CellIdentifier = @"ZombieCell";

 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 if (cell == nil)
 {
 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStyleDefault reuseIdentifier:CellIdentifier] autorelease];
 }

 NSDictionary *zombie = [self.knownZombies objectAtIndex:indexPath.row];

 cell.textLabel.text = [zombie objectForKey:@"name"];

 return cell;
}

That’s pretty much all there is to it. When you expose your WCF services as RESTful, JSON-serialized services then they become an absolute piece of cake to consume from your iOS application. One main reason for this is due to the availability of frameworks like the JSON framework. The other reason is that the XML parsing capabilities you get out of the box with the iOS SDK suck. Also, you might think the difference is small but when you get into large numbers of rows, the difference in payload size between XML and JSON can actually make a big impact on your application’s performance. The other (final) also is that if you are hosted on a site that charges by bandwidth, every dime saved by using JSON over XML counts.