How to make HTTP request from iPhone and parse JSON result

23 Comments

This example takes a simple view controller, calls the googlemaps api, and parses the returning JSON results.

The network call

NSURLConnection is the object that handles the HTTP request. You basically define the URL you are connecting to, implement the required delegates, and then get notified when the request is complete in the connectionDidFinishLoading method.

Parsing the JSON

NSJSONSerialization seems to currently be the best way to take your URL response and convert it into an iOS JSON object.

With this object you can then parse the response, looking at the keys and values, and then manually determine which elements you want to extract.

The code

Putting all that together, you get something that looks like this:

ViewController.m

#import "spike1ViewController.h"

@interface spike1ViewController()
@property (nonatomic, strong) NSMutableData *responseData;
@end

@implementation spike1ViewController

@synthesize responseData = _responseData;

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    NSLog(@"viewdidload");
    self.responseData = [NSMutableData data]; 
    NSURLRequest *request = [NSURLRequest requestWithURL:
                             [NSURL URLWithString:@"https://maps.googleapis.com/maps/api/place/search/json?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=AIzaSyAbgGH36jnyow0MbJNP4g6INkMXqgKFfHk"]];
    [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
  
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    NSLog(@"didReceiveResponse");
    [self.responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {        
    [self.responseData appendData:data]; 
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {    
    NSLog(@"didFailWithError");
    NSLog([NSString stringWithFormat:@"Connection failed: %@", [error description]]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSLog(@"connectionDidFinishLoading");
    NSLog(@"Succeeded! Received %d bytes of data",[self.responseData length]);
    
    // convert to JSON
    NSError *myError = nil;
    NSDictionary *res = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:&myError];
    
    // show all values
    for(id key in res) {
        
        id value = [res objectForKey:key];
        
        NSString *keyAsString = (NSString *)key;
        NSString *valueAsString = (NSString *)value;
        
        NSLog(@"key: %@", keyAsString);
        NSLog(@"value: %@", valueAsString);
    }
    
    // extract specific value...
    NSArray *results = [res objectForKey:@"results"];

    for (NSDictionary *result in results) {
        NSString *icon = [result objectForKey:@"icon"];
        NSLog(@"icon: %@", icon);
    }

}

- (void)viewDidUnload {
    [super viewDidUnload];
}

@end

You can tell it’s working by looking at the output in the NSLogs. Should see something that looks like this:

The world’s greatest tool for capturing and debugging Http traffic

4 Comments

Hyperbolic title aside, I have to share with you a tool that has proved invaluable not only for debugging Restful Http applications, but SOAP and anything else that uses Http.

TCPMon

TCPMon is a network monitoring tool that intercepts network traffic (http GETS and POSTS) before forwarding it on to its original destination.

It’s invaluable for debugging Http Rest or SOAP applications because it actually shows you all the traffic, headers, and post information flying between end points in your application.

How does it work?

Say we wanted to test the service we described in our last post which is configured to spin up a WCF end point listening on port 1980.

          <baseAddresses>
            <add baseAddress="http://localhost:1980" />
          </baseAddresses>

Connected to by a proxy that looked something like this:

            private IFooService CreateProxy()
            {
                EndpointAddress ep = new EndpointAddress("http://localhost:1980/abc");
		  ...	
		  
                return factory.CreateChannel();
            }

Instead of connecting directly to the endpoint directly:

you temporarily change your client proxy to point to the port TCPMon will be listening on (say 9090) and then have TCPMon forward that request off to port 1981 and return the corresponding response.

So our proxy client connection would look like this:

            private IFooService CreateProxy()
            {
                EndpointAddress ep = new EndpointAddress("http://localhost:9090/abc");
		  // TCPMon  listening here. Will forward to port 1980
		  ...	
		  
                return factory.CreateChannel();
            }

Setting up TCPMon to do this is dead easy. You just specify the port you want to listen on, and then the port you want to forward to. Click ‘Add’ and you are done.

Now when you run your tests you should now see something like this:

Beauty.

This tool has been invaluable for our team. It’s helps us create requests for automated integration tests, debug, and deal with a host of other issues that come up when building distributed applications.

It is written in Java, so if you don’t already have a Java JIT on your desktop you’ll need to install that first (download here).

Big thank you to Jonas Claesson for showing me this tool.

Good luck. And happy debugging!

A minimalist HTTP WCF configuration

4 Comments

Wanting a simple example of how to spin up and host a WCF endpoint and proxy for integration testing, I came up with the following simple example.

IFooService.cs


using System.ServiceModel;
using System.ServiceModel.Web;

namespace Test.SomeDir
{
    [ServiceContract]
    public interface IFooService
    {
        [WebGet(UriTemplate = "/ping")]
        [OperationContract]
        string Ping();
    }
}

FooService.cs


namespace Test.SomeDir
{
    public class FooService : IFooService
    {
        public string Ping()
        {
            return "Ping";
        }
    }
}

FooServiceTest.cs


using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
using System.Text;
using NUnit.Framework;
using Test.SomeDir;

namespace Test
{
    public class FooServiceTest
    {
        [TestFixture]
        public class When_spec
        {
            private ServiceHost host;
            private IFooService proxy;

            [SetUp]
            public void SetUp()
            {
                host = new WebServiceHost(typeof(FooService));
                host.Open();
                proxy = CreateProxy();
            }

            [TearDown]
            public void TearDown()
            {
                host.Close();
            }

            [Test]
            public void Should_observation()
            {
                proxy.Ping();
            }

            private IFooService CreateProxy()
            {
                EndpointAddress ep = new EndpointAddress("http://localhost:1980/abc");

                var binding = new CustomBinding(
                    new TextMessageEncodingBindingElement(MessageVersion.None, Encoding.UTF8),
                    new HttpTransportBindingElement { ManualAddressing = true });

                var factory = new ChannelFactory<IFooService>(binding, ep);
                factory.Endpoint.Behaviors.Add(new WebHttpBehavior());

                return factory.CreateChannel();
            }
        }
    }
}

The unit test above dynamically spins up a WCF WebServiceHost service which automatically sets some http bindings and behaviour configuration for us.

All we need to do now is add some information to our App.config file like this.

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <system.serviceModel>
    <services>
      <service name="Test.SomeDir.FooService"> <!-- this needs to be the fully qualified name of the service from your test -->
        <endpoint address="abc" binding="webHttpBinding" contract="Test.SomeDir.IFooService" /> <!-- address can be anything -->
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:1980" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
  
</configuration>

And voila! You are done.

Couple things to be aware of.

1. The name in the configuration has to exactly match fully qualified name used to spin up our host.

            [SetUp]
            public void SetUp()
            {
                host = new WebServiceHost(typeof(FooService));
                host.Open();
                proxy = CreateProxy();
            }

Needs to be expanded to the following in your config.

<service name="Test.SomeDir.FooService">

Get this wrong and you will get the following error:

System.InvalidOperationException : Service ‘Test.SomeDir.FooService’ has zero application (non-infrastructure) endpoints.

2. Note the end point address ‘abc’.

<endpoint address="abc"

This need to map to the path the immediately follows the base host address in your URL;

http://localhost:1980/abc

Hope this helps. I find writing a test like this useful for testing my end points and making sure I’ve got everything setup.

A good book for learning more WCF is Learning WCF: A Hands-on Guide

%d bloggers like this: