Launching Safari From Your App

This is one of the simplest things you can do, launch the Safari mobile browser from inside your iOS App.

Here’s how we’d launch it for SitterSat.

SitterSat AppStore Icon

SitterSat AppStore Icon

Let’s say I have a button on my app. The button is linked through the interface builder to a method that handles the click titled LaunchSafariButtonClick.  If I have any typos, sorry, I typed it out by hand real quick, but you get the idea I’m sure.

- (IBAction)LaunchSafariButtonClick:(UIButton *) sender {
  [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://www.sittersat.com"]];
}

Yep, you can use a UIWebView to display the page inside your app, but this is a quick way to launch Safari outside of your app.

Advertisements

Teleporting a Kangaroo

scott sappenfield

Remember this guy? He’s our friendly kangaroo lying around in the sun.

Well, he’s bored sitting in isolation in my iPhone photo gallery and wants to go up to the web to be shared with everyone. I haven’t looked up the definition of teleportation, but I have to imagine there’s a sender and a receiver.  So in this case, he’s going to be teleported from my iPhone (that’s the transmission sender) all the way up to the world wide web (that’s the transmission receiver).

Disclaimer

No actual animals have been harmed for the purposes of this post. Sorry, that’s another lame attempt at humor. It’s tough for me to make the nuts and bolts of technical stuff entertaining, but I try.

The Nuts and Bolts

Let’s see what’s going on with the code ( I took some code out, moved some other code around and added some commentary just for simplicity and clarity.)  In my actual code, there are things you don’t need to concern yourself with.  For instance, I start spinning an animator.  While that’s useful, you don’t need those details here.

The Sender

My iPhone.  Let’s send it on up to the receiver.  This method is dispatched in a separate thread, so keep that in mind.  If you don’t do that, make sure you do an asynch request so as to not tie things up unnecessarily.

(void)uploadMyNewPictureToWeb {

        //your web service that's on the lookout for Mr. Kangaroo
        NSMutableString *customWebPage = [NSMutableString stringWithString:@"your web service URL would go here"];
        NSURL *aUrl = [NSURL URLWithString:customWebPage];
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl];

        //this is your POST, PUT, whatever restful protocol you support
        [request setHTTPMethod:@"POST"];

        //set the content type
        //the spec calls for a unique string of characters as a separator, so use something random, but unique
        //also, I can't stress enough how important it is to get all the \r\n's correct
        NSString *boundary = @"DK39FJK4589FDKJSW893JKLR89DFJK238934IOSO";
        NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
        [request setValue:contentType forHTTPHeaderField:@"Content-Type"];

        //body
        NSMutableData *body = [NSMutableData data];

        //add all the textual params first (I'm going to assume you have some things to transmit other than just a photo
        //let's call them param1 and param2
        [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"param1"] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"%@\r\n", _param1] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"param2"] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"%@\r\n", _param2] dataUsingEncoding:NSUTF8StringEncoding]];

        //now add the image data, remember in the original post, I used a button, you could get this from local documents storage or wherever
        NSData *imageData = UIImagePNGRepresentation(_profilePictureButton.imageView.image);
        if (imageData) {
            [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
            [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"mrkangaroo.png\"\r\n", @"mrkangaroo"] dataUsingEncoding:NSUTF8StringEncoding]];
            [body appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
            [body appendData:[NSData dataWithData:imageData]];
            [body appendData:[[NSString stringWithFormat:@"%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        }
        [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

        //set the body to the request
        [request setHTTPBody:body];

        //set the content length
        NSString *postLength = [NSString stringWithFormat:@"%d", [body length]];
        [request setValue:postLength forHTTPHeaderField:@"Content-Length"];

        NSURLResponse *response;
        NSError *error;

        //send it and do something with the response...response parsing is left off here for simplicity
        NSData *myJSONNetworkData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

        //parsing response...
}

The Receiver

A web service keeping an eye out for Mr. Kangaroo.

You probably don’t need an example, but sometimes it helps to see how to handle things on the receiving end.  Whatever you use (ROR, PHP, JSP) on the receiving end will be able to easily parse this data.  SitterSat.com was written in ASP.Net/VB on the server-side, so here it is in that language. Again, I’ve removed a lot because you don’t need all that, just the relevant pieces.

//ASP.Net libraries have many useful collection for you, one being files in the request object
Dim fileCollection As HttpFileCollection = Request.Files

//Code works, but is simplified here for you, meaning you'll want more robust error handling and logging
For Each uploadedFileName In fileCollection
  Dim uploadedFile As HttpPostedFile = fileCollection(uploadedFileName)
  If Not uploadedFile Is Nothing Then
    If (uploadedFile.ContentLength > 0) Then
        //Save it somewhere on the server, whatever path conventions you have
        //also, this doesn't save it with a file extension, but you could that just the same
        uploadedFile.SaveAs(Server.MapPath("~/Pictures/mrkangaroo"))
    End If
  End If
Next

I hope this will help you out should you ever need to get into the business of transporting. Best of luck, let me know if I can help with anything.  You know, now that I’m thinking about it, I went to all this trouble, why didn’t I just tweet or FB him?  Oh well.

Complex URL connection

Previously, I posted how you can create a simple URL connection to something easy, say a UIWebView.  Let’s look at a more advanced example of making an HTTPS call from a button click and then parsing the response.

You can do this however you want of course, but for simplicity, let’s just work with an HTTPS call returning a HTML response.

Oh and a couple of notes:
– Let’s do this asynchronously so as to not hang up any main threads
– Let’s go ahead and demonstrate a POST while we’re at it
– Let’s define something to hold the URL response data

//something like this or however you choose to define it
@property (strong) NSMutableData *ReceivedData;
@synthesize ReceivedData = receivedData;

1. Let’s say in your Interface Builder, you have a button that responds to a click. Let’s say the name of the method is MyButtonClick. The code in here is very self-explanatory. You’ll notice in the code though that we’ve made ourselves, self, the delegate to response to the callbacks. Those methods will be provided following this piece of code.

- (IBAction)MyButtonClick:(UIButton *)sender {
    
    NSLog(@"Looking up information...");

    NSURL *aUrl = [NSURL URLWithString:@"https: your url goes here"]; 
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl]; 
    [request setHTTPMethod:@"POST"]; 

    NSMutableString *myInputValuesPostString = [NSMutableString stringWithString:@"myemail="];
    [myInputValuesPostString appendString:[myUserObject myEmailAddress]];
    NSString *postString = [NSString stringWithString:myInputValuesPostString];   

    NSLog(@"%@", postString);       
        
    NSData *myRequestData = [postString dataUsingEncoding:NSUTF8StringEncoding];
    [request addValue:@"8bit" forHTTPHeaderField:@"Content-Transfer-Encoding"];
    [request addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    [request addValue:[NSString stringWithFormat:@"%i", [myRequestData length]] forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:myRequestData];
        
    NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self]; 
        
    if (theConnection) {
        receivedData = [NSMutableData data];
        NSLog(@"Connection succeeded");
    } else {
        NSLog(@"Connection failed");
    }
}

2. Here’s your callbacks you’ll want to listen to

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
{ 
    NSLog(@"didReceiveResponse called");
    [receivedData setLength:0]; 
} 

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{ 
    NSLog(@"didReceiveData called");
    [receivedData appendData:data]; 
} 

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{ 
    NSLog(@"Connection failed! Error - %@ %@", 
          [error localizedDescription], 
          [[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]); 
} 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{ 
    NSLog(@"Connection succeeded! Received %d bytes of data",[receivedData length]); 
    NSString *responseText = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding]; 
    NSLog(@"%@", responseText);
}