Using NSURLProtocol with Swift
In this NSURLProtocol tutorial you will learn how to work with the URL loading system and URL schemes to add custom behavior to your apps. By Zouhair Mahieddine.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Update note: This tutorial was updated for iOS 8 and Swift by Zouhair Mahieddine, checked against Xcode 6.1.1. Original tutorial by Rocir Santiago.
NSURLProtocol is like a magic key to the URL. It lets you redefine how Apple’s URL Loading System operates, by defining custom URL schemes and redefining the behavior of existing URL schemes.
Does that sound magical? It should. Because, if you look for it, I’ve got a sneaky feeling you’ll find URLs — much like love — are all around us. What do UIWebView and WKWebView use? URLs. What’s used for video streaming with MPMoviePlayer? URLs. How do you send someone to your app on iTunes, initiate FaceTime or Skype, launch another app on the system, or even embed an image in an HTML file? With URLs. Have a peek at NSFileManager and notice how many of its file-manipulation methods require and return — URLs.
In this NSURLProtocol tutorial, you’ll learn how to define a protocol handler that modifies URL schemes. It will add a rough and ready transparent caching layer, by storing retrieved resources in Core Data. By enabling it, an ordinary UIWebView can then take on the role of a browser by caching downloaded pages for offline viewing at a later time.
Before you dive in head first, you’ll need a basic understanding of networking concepts and familiarity with how NSURLConnection
works. If you are not currently familiar with NSURLConnection
then I suggest reading this tutorial and/or this document by Apple.
So are you ready to learn what you can do with NSURLProtocol
? Good, go pour yourself a cuppa something and settle in for a meaty, mind-broadening discussion and step-by step exercise.
Getting Started
For this tutorial’s project, you’ll build an elementary mobile web browser, such as one that you might add to your next app. It will have a basic user interface that lets the user enter and go to a URL. The twist is that your browser will cache successfully retrieved results. This way the user can load pages already visited in the blink of an eye, because the page won’t load from a network request, but from the app’s local cache.
You already know that fast page loads == happy users, so this is a good example of how NSURLProtocol
can improve your app’s performances.
These are the steps you’re going to go through:
- Use a
UIWebView
for displaying the websites - Use Core Data for caching the results.
If you’re not familiar with Core Data, you can take a look into our tutorial. However, the code in this tutorial should be enough to understand the possibilities of NSURLProtocol
. Using Core Data is just a simple way to implement the local cache, so it’s not essential to learn something useful here.
Starter Project Overview
You can download the starter project here. As soon as the download is finished, unzip it and open the project file.
When you open the project, there are two main files. The first one is the Main.storyboard file. It has the UIViewController
set up the way you need for implementation. Notice the UITextField
(for URL input), UIButton
(for firing the web requests) and UIWebView
.
Open BrowserViewController.swift. Here you’ll see the basic behavior set up for the UI components. This UIViewController implements the UITextFieldDelegate
protocol, so you can fire the request when the user taps the return key. The IBAction for the button is pre-set to behave the same way as the return key. Last, the sendRequest()
method just takes the text from the textfield, creates a NSURLRequest object and calls the loadRequest(_:)
method from UIWebView to load it.
Once you’re familiarized with the app, build and run! When the app opens, enter “http://raywenderlich.com” and press the “Go” button. The UIWebView will load the response and display the results in the app. Pretty simple for a starting point. Now it’s time for you to stretch those finger muscles. Up next….coding!
Intercepting network requests
A set of classes known as the URL Loading System handles URL requests on iOS. At the heart of the URL Loading System is the NSURL
class. For network requests, this class tells what host your app is trying to reach and path to the resource at that host. In addition the NSURLRequest
object adds information like HTTP headers, the body of your message, etc.. The loading system provides a few different classes you can use to process the request, the most common being NSURLConnection
and NSURLSession
.
Now it’s time to start intercepting all NSURLRequest’s fired by the app. For that, you’ll need to create your own NSURLProtocol
implementation.
Click File\New\File…. Select iOS\Source\Cocoa Touch Class and hit the Next button. In the Class field, enter MyURLProtocol and in the Subclass of field, enter NSURLProtocol. Check that the language is set to Swift. Finally, press Next and then Create when the dialog appears.
Open MyURLProtocol.swift and replace its content with the following:
import UIKit
var requestCount = 0
class MyURLProtocol: NSURLProtocol {
override class func canInitWithRequest(request: NSURLRequest) -> Bool {
println("Request #\(requestCount++): URL = \(request.URL.absoluteString)")
return false
}
}
Every time the URL Loading System receives a request to load a URL, it searches for a registered protocol handler to handle the request. Each handler tells the system whether it can handle a given request via its canInitWithRequest(_:)
method.
The parameter to this method is the request that the protocol is being asked if it can handle. If the method returns true
, then the loading system will rely on this NSURLProtocol
subclass to handle the request, and ignore all other handlers.
If none of the custom registered handlers can handle the request, then the URL Loading System will handle it by itself, using the system’s default behavior.
If you want to implement a new protocol, like foo://, then this is where you should check to see if the request’s URL scheme was foo. But in the example above, you’re simply returning false
, which tells you your app cannot handle the request. Just hold on a minute, you’ll start handling them soon!
NSURLProtocol
is meant to be an abstract class. You create subclasses with the custom behavior for a URL protocol, but you never instantiate NSURLProtocol
directly.
Open AppDelegate.swift and replace the application(_:didFinishLaunchingWithOptions:)
method with this one:
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
NSURLProtocol.registerClass(MyURLProtocol)
return true
}
Now when your app launches, it will register the protocol with the URL Loading System. That means it will have the opportunity to handle every request delivered to the URL Loading system. This includes code which calls the loading system directly, as well as many system components that rely on the URL loading framework, such as UIWebView.
Build and run the project. Insert http://raywenderlich.com as the website, tap on Go and check the Xcode console. Now, for every request the app needs to perform, the URL Loading System asks your class if it can handle it.
In the console you should see something like this:
Request #0: URL = http://raywenderlich.com/ Request #1: URL = http://raywenderlich.com/ Request #2: URL = http://raywenderlich.com/ Request #3: URL = http://raywenderlich.com/ Request #4: URL = http://raywenderlich.com/ Request #5: URL = http://raywenderlich.com/ Request #6: URL = http://www.raywenderlich.com/ Request #7: URL = http://www.raywenderlich.com/ Request #8: URL = http://www.raywenderlich.com/ Request #9: URL = http://www.raywenderlich.com/ Request #10: URL = http://www.raywenderlich.com/ Request #11: URL = http://www.raywenderlich.com/ Request #12: URL = http://raywenderlich.com/ Request #13: URL = http://cdn3.raywenderlich.com/wp-content/themes/raywenderlich/style.min.css?ver=1402962842 Request #14: URL = http://cdn3.raywenderlich.com/wp-content/plugins/swiftype-search/assets/autocomplete.css?ver=3.9.1 Request #15: URL = http://cdn4.raywenderlich.com/wp-content/plugins/videojs-html5-video-player-for-wordpress/plugin-styles.css?ver=3.9.1 Request #16: URL = http://vjs.zencdn.net/4.5/video-js.css?ver=3.9.1 Request #17: URL = http://cdn3.raywenderlich.com/wp-content/themes/raywenderlich/style.min.css?ver=1402962842 ...
For now, your class is just logging the string representation of the request’s URL and returning false
, which means your custom class cannot handle the request. But if you look into the logs, you’ll see all the requests made from the UIWebView. It includes the main website (.html) and all the assets, such as JPEGs and CSS files. Every time the UIWebView needs to fire a request, it’s logged to the console before it’s actually fired. The count should show you a mountain of requests — likely over five hundred — because of all the assets on the web page.
So this is your way in: your custom class is being notified for every URL request, and next you can do something about each request!