Tutorial: Building a Web Browser with UIWebView (Part 3)

Screenshot of the Complete WebBrowser Tutorial

Figure 1: Screenshot of the Completed WebBrowser Tutorial

This tutorial is part three of a three part tutorial on creating a web browser using UIWebView (see Part 1 & Part 2). A screenshot of the completed project is shown in Figure 1.

If you completed part two of the tutorial and it seems to be working well you can continue with your current project, or if you prefer you can download the starting point here: WebBrowser2.zip

Outstanding Issues

In this tutorial, you will fix the issues noted at the end of part two, I have reordered them slightly so that dependent issues are dealt with in order:

  • the keyboard displayed is not the URL keyboard so it is awkward to type a URL,
  • the keyboard auto-capitalization mode is inappropriately set,
  • there is no clear button in the address field,
  • if the user omits the http:// the page will not load,
  • the address field can get out of sync with the displayed page,
  • if an error occurs, the user is never informed.

Fixing the Keyboard Configuration

Currently the keyboard type that appears is not really suitable for entering URLs. Much of the punctuation needed requires switching to another keyboard.

The keyboard type that appears when any subclass of UIResponder becomes first responder is controlled by the keyboardType of the object. Possible values are:

  • UIKeyboardTypeDefault
  • UIKeyboardTypeASCIICapable
  • UIKeyboardTypeNumbersAndPunctuation
  • UIKeyboardTypeURL
  • UIKeyboardTypeNumberPad
  • UIKeyboardTypePhonePad
  • UIKeyboardTypeNamePhonePad
  • UIKeyboardTypeEmailAddress

In your case you can make the appropriate keyboard appear by inserting the following line in viewDidLoad in WebBrowserViewController.m.

    address.font = [UIFont systemFontOfSize:17];
    address.keyboardType = UIKeyboardTypeURL;
    [address addTarget:self 
                action:@selector(loadAddress:event:) 
      forControlEvents:UIControlEventEditingDidEndOnExit];
    [navBar addSubview:address];

If you compile and run your code now you should find that the keyboard that appears has ‘/’ and ‘.com’ keys.

There are still problems with the keyboard. When you use backspace to erase the currently displayed URL the keyboard automatically switches to uppercase.

The keyboard auto-capitalization behavior is controlled by the autocapitalizationType property of the first responder. Possible values are:

  • UITextAutocapitalizationTypeNone
  • UITextAutocapitalizationTypeWords
  • UITextAutocapitalizationTypeSentences
  • UITextAutocapitalizationTypeAllCharacters

Add this line of code after the one you just added:

    address.autocapitalizationType = 
                UITextAutocapitalizationTypeNone;

Compile and run your project and confirm that the auto-capitalization behavior is now correct.

Adding a Clear Button to the Address Field

As it stands, if the address field is already displaying an address you have to backspace until you have erased the entire address. This is quite tedious. In Safari when you edit an address an ‘X’ in a grey circle appears on the right hand side of the address field. Touching this icon erases the complete field. Clearly this behavior is more desirable.

The clearButtonMode property of UITextField controls when this “clear button” is displayed. Possible values are:

  • UITextFieldViewModeNever
  • UITextFieldViewModeWhileEditing
  • UITextFieldViewModeUnlessEditing
  • UITextFieldViewModeAlways

In your case, the closest match to the behavior exhibited by Safari is UITextFieldViewModeWhileEditing.

Just after the last line of code you added add:

address.clearButtonMode = 
             UITextFieldViewModeWhileEditing;

Save everything, compile and run your project now. You should see the clear button appear when you start editing a URL.

Correcting User Input

Most modern web browsers allow the user to drop the “http://” prefix on URLs. At this point, if a user omits this prefix in your browser the page will not load. (In fact, an error will be generated but will have pushed off dealing with errors until the end of the tutorial.)

It is fairly simple to fix this. Edit your loadAddress method as follows.

- (void)loadAddress:(id)sender event:(UIEvent *)event
{
    NSString* urlString = self.addressField.text;
    NSURL* url = [NSURL URLWithString:urlString];
    if(!url.scheme)
    {
        NSString* modifiedURLString = [NSString stringWithFormat:@"http://%@", urlString];
        url = [NSURL URLWithString:modifiedURLString];
    }
    NSURLRequest* request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
}

This solution uses the scheme property of the NSURL class to determine whether the user set a scheme (the part of the URL before the colon). If not it prepends “http://” this will correct entries like www.yahoo.com to http://www.yahoo.com.

Compile and run your your code. Enter “www.yahoo.com” and verify that the correct web page is loaded.

Keeping the Address Field in Sync

If you use the back or forward buttons, or follow a link, the address bar is not in sync with the displayed page. It turns out there is more to solving this problem than meets the eye. So first define a method to update the address bar.

Add the following definition to WebBrowserViewController.h:

- (void)updateAddress:(NSURLRequest*)request

The implementation of this method looks like this. Add this to your WebBrowserViewController.m.

- (void)updateAddress:(NSURLRequest*)request
{
    NSURL* url = [request mainDocumentURL];
    NSString* absoluteString = [url absoluteString];
    self.addressField.text = absoluteString;
}

The key point about this code is that instead of requesting the URL from NSRequest you are requesting the mainDocumentURL which is more appropriate to show in the address field.

So this code solves how to display an updated URL in the address field, but not how and when you will be informed.

On the face of it, it should be simple. According to Apple documentation the delegate method webView:shouldStartLoadWithRequest:navigationType: is called before the web view starts loading content.

So the following code modification should ensure that your address field is always in sync with the currently displayed page.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    [self updateAddress:request];
    return YES;
}

It turns out that this works most of the time, except when you use the “back” and “forward” buttons. It seems that sometimes calling goBack or goForward does not call shouldStartLoadWithRequest.

A reasonable workaround for this problem is to add the following code to your webViewDidFinishLoad:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    [self updateButtons];
    [self updateTitle:webView];
    NSURLRequest* request = [webView request];
    [self updateAddress:request];
}

Reporting Errors to the User

When UIWebView encounters an error it invokes the didFailLoadWithError method of its delegate.

You could handle the error right in the delegate method, but as your programs get bigger having a central error handling routine will be useful (a more general solution to this will be the topic of a future tutorial). So just add the highlighted code to your didFailLoadWithError.

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    [self updateButtons];
    [self informError:error];
}

Since this method is not yet declared you should add a declaration in your header file.

- (void)informError:(NSError*)error;  

The UIAlertView class provides a convenient way of alerting users of errors.
Add the following code to WebBrowserViewController.m.

- (void)informError:(NSError *)error
{
    NSString* localizedDescription = [error localizedDescription];
    UIAlertView* alertView = [[UIAlertView alloc] 
                              initWithTitle:@"Error" 
                                    message:localizedDescription delegate:nil 
                          cancelButtonTitle:@"OK" 
                          otherButtonTitles:nil];
    [alertView show];
    [alertView release];
}

The NSError class offers a variety of messages, but for your simple error handler the localizedDescription is the most appropriate. As regards the UIAlertView since there is no other sensible action that the user can do other than acknowledge the error a simple “OK” button will suffice, that is, “retry” or other options probably don’t make sense here.

Once you’ve made this change you can compile and run your project. You should now have a fully functional, albeit at a basic level, web browser.

Conclusion

Over the the three tutorials of this project you have learned a lot. You have used Interface Builder to layout a basic UI that contained a UIToolbar, a UIWebView and a number of UIBarButtonItems. You augmented this UI programmatically with a UINavigationBar, UILabel and a UITextField. To keep your UI in sync with the UIWebVIew your UIViewController subclass implemented the UIWebViewDelegate protocol and, when thing went wrong, you used UIAlertView to inform the user. Not bad for a few hours work!

Download the Source Code

You download the source code for the completed tutorial here: WebBrowser3.zip

This web site is ad supported. If you would like to see other great tutorials like this one please visit one of our sponsors.


About idz

A professional software engineer dabbling in iOS app development.
This entry was posted in Tutorial and tagged , , , , , , . Bookmark the permalink.

12 Responses to Tutorial: Building a Web Browser with UIWebView (Part 3)

  1. Pingback: How to detect redirect in a UIWebView - Programmers Goodies

  2. dayabaran says:

    Create tutorial. Thank you very much. How do I parse an xml feed from the server to a UITableView using xCode4

    • idz says:

      Hi Daya,

      That’s a little more than a tutorial, but basically you create an NSURL pointing to the server. Using that you create an NSMutableURLRequest, then you create an NSURLConnection with the request. The connection delegate will be called back as the XML response is received and you parse that into a data structure that you use to update your UITableView.

      If you search the Apple documentation for the classes I mention above you should be able to figure it out.

      Hope this helps,
      idz

  3. Srinivas HN says:

    Hi it was an amazing tutorial i followed your tutorial to understand the UIWebview and created the similar browser as you created.I want to go little further in this like to build a actual browser where in it is ahving bookmarks history. I want some help from you like how can i proceed to develop a bookmarks or histroy functionalities in custom web browser.

    Thanks in advance for your any kind of reply

    • idz says:

      Hi Shrinivas,

      To implement a history you could keep track of however many URLs you want in an NSMutableArray. You could add another button to the toolbar to allow the user to call up a view that would allow them to choose a URL to jump to. A UITableView would be suitable for displaying the history items. If you want to save the history between runs you would have to save the contents of the NSMutableArray to either the users preferences using NSUserDefaults or a file.

      To implement bookmarks you could a similar method or you could use CoreData or SQLite to keep the bookmarks in a database.

      Hope this helps,
      idz
      P.S. Sorry for the delay in replying. Haven’t had much time to log onto the site recently.

  4. 4ki says:

    Hi idz,
    It was a great tutorial!!
    I’m new to this development but I tried to follow and understand your tutorial.

    I developed a browser by following your tutorial. I tried to take out the addressField but keep only page title. When I did this the page title was missing.

    Would you mind to help how to do this?

    Thanks in advance.

    • idz says:

      Hi 4ki,

      Details of how the navigation bar with the page title and addressField are in tutorial 2. If you follow these instructions and do not create the UITextField used for the address bar you should be able to achieve what you are trying to do. Make sure you still create the navBar. You will need to adjust the frame of the navBar so that there is not an empty space where the address bar is mean to be.

      Hope this helps,
      idz

  5. 4ki says:

    Hi idz,

    Many THANKS!

  6. CaldwellYSR says:

    This is a great tutorial and it was very interesting to see how to programmatically make the navigation bar instead of using the interface builder. Glad to have learned something new. I’m curious about something though. I don’t have any experience working with the gesture recognizer but I want to make the forward and back buttons work on swipes to the left and right. Similar to how Chrome Mobile switches tabs with swipes but I want to go forward and back.

    Anyways do you know of any resources I could use to learn about something like this?

    • idz says:

      Hi Caldwell,

      Glad you found it useful. Apart from just googling for UIGestureRecognizer I am not sure where you can find a good tutorial on the subject, but I think it is a great idea for an extension to this tutorial. I will try to add it to the site in the next few days.

      • CaldwellYSR says:

        Awesome, I look forward to it. I’ve been fiddling myself a bit but so far I’m failing. I can get the UIGestureRecognizer to fire sometimes but not consistently. One of the main issues is that UIWebView has its own UIGestureRecognizers that get the priority. I’ll let you know if I come up with anything.

        • idz says:

          Hi Caldwell,
          Had some time this morning so put up a tutorial showing how to do UIGestureRecognizers for forward and back. I did not run into the problems you mentioned with reliability. I wonder if maybe it only occurs when the page itself is trying to capture gestures (via JavaScript or something).
          Anyway hope this is useful to you.

Leave a Reply