Adding Web Content to Your App (UIWebView), Part IV — Is There Internet?

This post answers the following questions:

  1. How can I check if the device is connected to the Internet?
  2. What do I do to make sure my app doesn’t crash when I access Internet resources?

If your application uses the Internet, but crashes if the user is not connected, Apple may reject your app upon App Store submission.  It’s therefore your responsibility to check to make sure Internet resources are available before you attempt to use them.  Unfortunately, there’s not a simple application variable or function to accomplish this check.

There are many ways of doing this out there, and our method is a little bit different.  It leverages the tools we learned in the previous 3 parts to this tutorial, presents the user with a clean, “Checking for Internet…” message, and leaves us with a more stable app.

Create a UIWebView as we did in the past three posts, and then add a boolean member variable and two more functions — in your .h:

BOOL internetOK;
...
- (void)tryInternet;
- (void)verifyInternet;

…and your .m:

- (void)tryInternet {
  internetOK = NO;
  [wWw stringByEvaluatingJavaScriptFromString:@"window.location='http://www.fourtentech.com/';"];
}

- (void)verifyInternet {
  if(!internetOK) {
    // no Internet access -- display error or take other action
  }
}

We’ll also need to include a new UIWebViewDelegate method:

- (void)webViewDidFinishLoad:(UIWebView *)webView {
  internetOK = YES;
}

What we’ve got here is a “check” function that sets our “Internet is OK” flag to NO and then tries to load a page.  If the page loads, webViewDidFinishLoad: is called and our flag is set to YES.  After a certain number of seconds (a timeout), we’ll call verifyInternet to see if our flag is set to YES, and if not, we know there’s been a failure.

To make this happen, present the user with a UIWebView with a message that you’re checking for Internet access (you can also do this with a hidden UIWebView and do it in the background!).  Here’s one we used in our own iPhone app:

[wWw loadHTMLString:@"<body bgcolor='#DDDDDD'><font face='Helvetica'><br /><br /><br /><br /><br /><font color='#3080A0'><center><b>Connecting to FourTen's server...</b><center></font></font></body>" baseURL:nil];

Then set our two functions to run:

[self performSelector:@selector(tryInternet) withObject:nil afterDelay:1.0];
[self performSelector:@selector(verifyInternet) withObject:nil afterDelay:15.0];

The first one we run after 1 second, because if you try to run it simultaneously with loading the HTML string, it won’t work.  The second one we run after 15 seconds, making our timeout 14 seconds.  You can choose to wait more or less time.  After calling the first, internetOK will only be YES if Internet is detected.

This is the end of a four-part series on the UIWebView — we hope it’s been useful!

FourTen Technologies, Inc., is a leading US iPhone app development firm. For information on having FourTen build a custom mobile application for your company, visit www.fourtentech.com. Article written by Jonathan Corbett (President & CEO, FourTen). Contact: jcorbett@fourtentech.com.

Adding Web Content to Your App (UIWebView), Part III — Sending Messages

This post answers the following questions:

  1. How can I send a message from a UIWebView to my class?
  2. How can I take an action based on an event in a UIWebView?

In the last post, we discussed creating navigation for a UIWebView, and used the stringByEvaluatingJavaScriptFromString: function as well as the shouldStartLoadWithRequest: delegate method.  In this post, we’re going to use both of those functions to get messages from the UIWebView to your Objective-C code.

The first way to get a message from your UIWebView is asynchronous.  All one must do is query the DOM using JavaScript.  Let’s say you had the following in your HTML:

<input id="fourten" />

…and you wanted to find out what a user had typed in this text box.  All one would need to return the contents of the text box is:

[wWw stringByEvaluatingJavaScriptFromString:@"document.getElementById('fourten').value;"];

This is great if you just need to check the present value of something.  If you want to know when a user takes an action within the UIWebView, you’ll need a little bit more than this; otherwise, you’d be polling the UIWebView constantly.  We can accomplish this using the other function we talked about last week.

Set up your UIWebView, and load in the following HTML:

<form action="http://message/">Tell me something: <input id="fourten" /><br /><input type="submit" value="Go!" /></form>

This naturally presents a text box and a submit button, and when the submit button is pressed, the data is sent to http://message/, a fictitious address.  But, before the data can be sent, the iPhone platform will consult shouldStartLoadWithRequest: to find out if we should proceed with that page load.  Instead, we’ll hijack that form submission and run our own code:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request {
  if([[request.URL absoluteString] isEqualToString:@"http://message/"]) {
    // do stuff
    return NO;
  }
}

  return YES;

The code above detects when the user is attempting to hit our fictitious address.  We can perform an action and then return NO to let the UIWebView know that it need not actually load our fictitious address.  All other attempts to load a page are met with a YES return.

You may also (and frequently will want to!) combine these two methods: use the later to know when to read the data, and the former to read the data.  That is, in place of // do stuff you can use the stringByEvaluatingJavaScriptFromString: function to get data out of as many form fields (including hidden form fields) as you’d like.

This is a four-part series on the UIWebView — please check back for more if you’re looking to go deeper.

FourTen Technologies, Inc., is a leading US iPhone app development firm. For information on having FourTen build a custom mobile application for your company, visit www.fourtentech.com. Article written by Jonathan Corbett (President & CEO, FourTen). Contact: jcorbett@fourtentech.com.

Adding Web Content to Your App (UIWebView), Part II — Where Am I?

This post answers the following questions:

  1. How can I build navigation (Forward, Back, etc.) for my UIWebView?
  2. How do I know what page the Web view is currently showing / user is viewing?
  3. How can I build a full “History” feature for a UIWebView?

In the last post, we discussed the basics of implementing the UIWebView.  Here, we’re going to move forward to deal with navigation issues relating to the UIWebView.  When you implement a UIWebView, all that you get is a view that displays HTML content.  There are no forward or back buttons, no history, etc.  These all have to be implemented by you.

Start by dragging (or coding, if you’d prefer sans-IB) a UIWebView onto your view, but leave some room at the top for buttons.  Then, drag 2 UIButtons onto the top of your screen, double click each, and set the text to Back and Forward.  Create an outlet for each, as well as a function to receive the “press” event, Touch Up Inside.

IBOutlet UIButton *bBack, *bForward;
IBOutlet UIWebView *wWw;

...
- (void)dontTouchMe:(id)sender;

Assign the dontTouchMe: function to Touch Up Inside by right-dragging from the UIButtons to the View Controller (File’s Owner).   Assign the View Controller to be the delegate for the UIWebView by the same action.  Lastly, assign the outlets by the reverse drag.

Next, meet the stringByEvaluatingJavaScriptFromString: function.  This handy function lets you evaluate any JavaScript of your choosing at any time on a given UIWebView.  There also happens to be a JavaScript function to go back and to go forward.

Create your dontTouchMe: function as follows:

- (void)dontTouchMe:(id)sender {
  if(sender == bBack)
    [wWw stringByEvaluatingJavaScriptFromString:@"history.go(-1);"];
  else if(sender == bForward)
    [wWw stringByEvaluatingJavaScriptFromString:@"history.go(1);"];

}

Save and build, and your back and forward navigation are now functional!

If a simple back/forward aren’t enough and you’re looking to create a full browser history, you’ll need a way to figure out where the user is.  You could use the same function we used above to detect the page a user is currently viewing.  For example,

NSLog(@"%@", [wWw stringByEvaluatingJavaScriptFromString:@"document.location"]);

…will log to the console the address of the current page the user is browsing.  However, this isn’t particularly useful to us, because we need to know when the user changes pages.  We could call this function once a second and try to detect a change, but that would be a waste — there’s an easier way!

The UIWebView’s delegate can accept messages regarding a page change using the shouldStartLoadWithRequest: function.  This function allows you to detect what page the user is trying to go to before the load actually happens, and you can even refuse the page change by simply returning NO.

Declare an NSMutableArray as a member variable and let the SDK know that your View Controller conforms to the UIWebViewDelegate protocol.  In your header file:

@interface AppNameViewController : UIViewController<UIWebViewDelegate> {
  NSMutableArray *aHistory;
...

Note the protocol within brackets.

Allocate and initialize the array in your viewDidLoad function:

aHistory = [[NSMutableArray alloc] init];

…and don’t forget to release the array when your View Controller is done with it.

Next, we’re going to implement the function we mentioned above:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
  [aHistory addObject:[[NSString alloc] initWithString:[request.URL absoluteString]]];
  return YES;

}

What we’ve done here is taken the URL out of the user’s request (absoluteString returns the full URL, including protocol and server), created a new string, and added it to our array.  After, we return YES to allow the UIWebView to go to the new page.  Your aHistory object now contains a full accounting of your browsing.

For a more real-world history, you’d probably also want to code such that if you push the back button, you don’t add another item to the history, but rather simply “go backwards” in the history.  To do this, simply add an int member variable to keep track of your position within the history array, and then before adding a new object to the array, check to see if the previous object ([aHistory objectAtIndex:x-1]) is equal to the new request.  If so, instead of adding the new object, simply decrement the counter.

This is a four-part series on the UIWebView — please check back for more if you’re looking to go deeper.

FourTen Technologies, Inc., is a leading US iPhone app development firm. For information on having FourTen build a custom mobile application for your company, visit www.fourtentech.com. Article written by Jonathan Corbett (President & CEO, FourTen). Contact: jcorbett@fourtentech.com.

Adding Web Content to Your App (UIWebView), Part I — Loading a Page

This post answers the following questions:

  1. How do I show a Web page within an app?
  2. How can I have a Web browser without launching Safari?
  3. How can I use an image file from my app bundle inside a UIWebView?

If you’re looking to show HTML content within your app, you’ll be pleased to know that Apple makes things incredibly easy in this regard. The UIWebView object provides you with roughly the same functionality as the standard Safari browser without having to lift a finger.

To begin, either use Interface Builder (IB) to drag a UIWebView onto the desired view, or add it to your view in code:

UIWebView *wWw = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
[self.view addSubview:wWw];

This will add a full-screen UIWebView to the current view. If you added your view in code, don’t forget to release the object you just allocated when you’re done! You’ll also want to set the delegate for the UIWebView to the current view controller. What this does is it tells the UIWebView that whenever an event happens, it’s the current view controller that gets notified (this should probably be a default, but it’s not!). In IB, drag using the right mouse button from the UIWebView to the View Controller (File’s Owner) and select “delegate”…

…or in code:

wWw.delegate = self;

If you’re using IB, you’ll also need to set up an outlet (a variable that corresponds to your UIWebView instance). In the class definition, add in the following:

IBOutlet UIWebView *wWw;

…and then right drag from the View Controller to the UIWebView and select “wWw.”

Now that your Web view is present and all wired up, you probably want some content in it. You can load content from a local HTML string or from the Internet, both without much difficulty.

To load from an HTML string, you’re going to use the loadHTMLString: method, which looks like this:

[wWw loadHTMLString:@"<h1>Visit <a href='http://www.fourtentech.com'>FourTen</a> for mobile application development!</h1>" baseURL:nil];

If you’re simply trying to display HTML formatted content, you’re actually done! You can simply edit the HTML string as desired, and your markup will be displayed as it would in Safari.

If you want to embed images that you’ve put in your app bundle within your HTML, you’ll also need to set the baseURL. Normally when you go to a Web page, you type in an address, or click on a link that contains an address, and your browser “knows” where you are. If you were to type:

<img src='410logo.png' />

…in your HTML, your browser would know that you really want to show http://[server]/[current_dir]/410logo.png. When you’re loading an HTML string into a UIWebView, there’s no way for the UIWebView to know where it is unless you tell it, and if you want local images to show, you simply pass it the path of the local app bundle:

[wWw loadHTMLString:@"<center><img src='410logo.png' style='margin-top:150px;' /></center>" baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];

We’re all about succinct code here at FourTen, so we actually did several things in that one line above. We got the application bundle path using [[NSBundle mainBundle] bundlePath], and then we used that to create an NSURL object. That NSURL object becomes our new baseURL, and now, local images will show. The code above will get you:

To show a remote Web page, it’s just as easy, if not easier. You’ll need the following:

[wWw loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.fourtentech.com/"]]];

We’ve also done several things in this line: we created a NSURL from a string, used that to create an NSURLRequest, and fed that to loadRequest. As long as your device or simulator has Internet access, you’ll end up here:

This is a four-part series on the UIWebView — please check back for more if you’re looking to go deeper.

FourTen Technologies, Inc., is a leading US iPhone app development firm. For information on having FourTen build a custom mobile application for your company, visit www.fourtentech.com. Article written by Jonathan Corbett (President & CEO, FourTen). Contact: jcorbett@fourtentech.com.

Welcome to Learn iPhone App Development with FourTen Technologies!

Welcome!  We’re kicking off the new year by giving back to the development community.  iPhone development, as far as we’re concerned, is still a brand new field, and even the most experienced iPhone developers out there have been at it for only 3+ years.  So, we’re all still learning, and FourTen extends its 10+ years of Web and software development and 3 years of iPhone development experience to you — free.

If you’ve not yet heard of FourTen, we are a leading US software development firm in the field of mobile applications.  When mobile platforms became powerful in 2007, our customers started asking us if we could put our development experience towards the new market.  Today, not even 4 years later, 80% of our revenue is mobile.  And we love it.

We’re a 100% US company, we don’t outsource, and our staff in New York and Miami have learned a lot over the last few years.   We hope it helps!