How do I implement offline caching of web pages step by step?

How do I implement offline caching of web pages step by step?

How to implement offline caching strategy for a hybrid app? In simple terms, your app is just a shell, and the actual content loaded inside is H5. How to optimize the speed of loading content?

First, let’s take a look at NSURLProtocol

It looks like a protocol, but it is actually a class that inherits from NSObject. Its function is to handle the loading of specific URL protocols. It is an abstract class that provides the infrastructure for handling URLs using characteristic URL schemes. You can create a subclass of NSURLProtocol to support custom protocols or URL schemes in your application.

Applications never need to instantiate an NSURLProtocol subclass directly. When a download begins, the system creates an appropriate protocol object to respond to the URL request. All you have to do is define your own protocol and then call registerClass: when your app starts to let the system know about your protocol.

Note: You cannot customize URL schemes and protocols in watchOS 2 and later.

To support specific custom requests, you can first define NSURLRequest or NSMutableURLRequest. To make these custom objects implement the request, you need to use NSURLProtocol's propertyForKey:inRequest: and setProperty:forKey:inRequest, and then you can customize the NSURLResponse class to simulate the return information.

Next, we will start offline caching of UIWebView.

UIWebView offline cache processing

First, we need to customize a subclass of NSURLProtocol and in AppDelegate.m

  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  2. [NSURLProtocol registerClass:[ZGURLProtocol class]];
  3. return YES;
  4. }

Register. All the following operations are performed in our custom ZGURLProtocol. Let's first look at the role of registerClass:

Try registering a subclass of NSURLProtocol to make it visible to the URL Loading System. The URL Loading System is a set of classes and protocols that allow your application to access content generated by URLs, such as requests, received content, and caches. When the URL Loading System starts loading a request, each registered protocol class is called in turn to determine whether it can be initialized with the specified request. The first method called is:

  1. + (BOOL)canInitWithRequest:(NSURLRequest *)request;

Perform cache filtering in this method. For example, if you want to cache only js, then determine the suffix of the request path. If it is js, return YES, otherwise return NO.

If YES is returned, it means that the request is handled by the custom URLProtocol. There is no guarantee that all registered NSURLProtocols can be handled. If you define multiple NSProtocol subclasses, these subclasses will be called in reverse order. That is to say, if you write:

  1. [NSURLProtocol registerClass:[ZGURLProtocol class]];  
  2. [NSURLProtocol registerClass:[ZProtocol class]];

Then the first thing executed is ZProtocol. If the initWithRequest: parameter returns YES, the request is processed by ZProtocol and does not go through ZGURLProtocol. If the initWithRequest: of ZProtocol returns NO, the request continues to be passed down and processed by other NSURLProtocol subclasses.

Once YES is returned, the request will be handled by your own subclass, which will first call:

  1. + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request

This is an abstract method that subclasses must implement. Normally, we return the request directly, but you can also modify the request directly, including headers, hosts, etc. You can redirect the specified request.

Here, we just return the existing request.

Then, the request will begin:

  1. - (void)startLoading;

The function of this method is to start requesting the request specified by the protocol. This method is also a method that the protocol subclass must implement. The operations performed here are:

First determine whether there is cached data. If so, create an NSURLResponse yourself, then put the cached data in, perform some client operations, and then return; if there is no cached data, create a new NSURLConnection and then send the request.

Let's first talk about the case where there is a cache:

  1. if (model.data && model.MIMEType) {
  2. NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.request.URL MIMEType:model.MIMEType expectedContentLength:model.data.length textEncodingName:nil];
  3. [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
  4. [self.client URLProtocol:self didLoadData:model.data];
  5. [self.client URLProtocolDidFinishLoading:self];
  6. return ;
  7. }

(model is cached data) If there is a cache, directly use the cached data and MIME type, then build NSURLResponse, and then call the proxy method through the protocol client. The client here is a protocol, as follows:

  1. @protocol NSURLProtocolClient <NSObject>  
  2. - (void)URLProtocol:(NSURLProtocol *)protocol wasRedirectedToRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse;
  3. - (void)URLProtocol:(NSURLProtocol *)protocol cachedResponseIsValid:(NSCachedURLResponse *)cachedResponse;  
  4. - (void)URLProtocol:(NSURLProtocol *)protocol didReceiveResponse:(NSURLResponse *)response cacheStoragePolicy:(NSURLCacheStoragePolicy)policy;
  5. - (void)URLProtocol:(NSURLProtocol *)protocol didLoadData:(NSData *)data;  
  6. - (void)URLProtocolDidFinishLoading:(NSURLProtocol *)protocol;  
  7. - (void)URLProtocol:(NSURLProtocol *)protocol didFailWithError:(NSError *)error;  
  8. - (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;  
  9. - (void)URLProtocol:(NSURLProtocol *)protocol didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;  
  10. @ end  

This protocol provides an interface for NSURLProtocol subclasses to communicate with the URL Loading System. An app must not implement this protocol. If there is a cache, the callback method is called and then processed.

Without caching:

Instantiate a connection and then initiate a request. When we receive the response:

  1. - (void) connection :(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *)response {
  2. self.responseData = [[NSMutableData alloc] init];
  3. self.responseMIMEType = response.MIMEType;
  4. [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
  5. }

Next comes receiving data:

  1. - (void) connection :(NSURLConnection *) connection didReceiveData:(NSData *)data {
  2. [self.responseData appendData:data];
  3. [self.client URLProtocol:self didLoadData:data];
  4. }

After receiving the data, it calls:

  1. - (void)connectionDidFinishLoading:(NSURLConnection *) connection {
  2. ZGCacheModel *model = [ZGCacheModel new];
  3. model.data = self.responseData;
  4. model.MIMEType = self.responseMIMEType;
  5. [self setMiType:model.MIMEType withKey:[self.request.URL path]];//userdefault stores MIMEtype
  6.      
  7.      
  8. [[ZGUIWebViewCache sharedWebViewCache] setCacheWithKey:self.request.URL.absoluteString value:model];
  9.    
  10. [self.client URLProtocolDidFinishLoading:self];
  11. }

This method ends the call after the home, and we need to cache the requested data here. In this way, we have the return data of the specified URL locally.

There is one important thing that has not been introduced here, that is

  1. [NSURLProtocol propertyForKey:ZGURLProtocolKey inRequest:request]  
  2. [NSURLProtocol setProperty:@YES forKey:ZGURLProtocolKey inRequest:mutableRequest];

Here

  1. + (void)setProperty:(id)value forKey:(NSString *) key inRequest:(NSMutableURLRequest *)request;

The function is to set the value associated with a specific key in the specified request. Prevent multiple calls to a request.

In this way, we have completed the offline cache of UIWebView. Here I encapsulate a ZGUIWebViewCache. If you are interested, you can take a look.

Offline cache processing of WKWebView

WKWebView offline cache is similar to UIWebView cache, except that after calling NSURLProtocol's canInitWithRequest: method at the beginning, subsequent requests seem to have nothing to do with NSURLProtocol. It is said on the Internet that WKWebView's requests are in an independent process, so NSURLProtocol is not used. Here, it is processed by NSURLProtocol+WKWebView class. For details, see: ZGWKWebViewCache .

The rest of the process is similar to UIWebView cache processing.

The above is the implementation of offline caching of web pages.

<<:  Android skinning principle and Android-Skin-Loader framework analysis

>>:  The seventh episode of the Aiti Tribe live class: How to build isomorphic applications using React

Recommend

Where might carcinogens be hidden in our lives?

Dangerous carcinogens are hidden everywhere in ou...

Kidney disease can not eat soy products? It's all a misunderstanding

Diet management is an extremely important part of...

C# 7 Feature Preview

[[165106]] Over the past year, we have shown read...

Niu Ge Practical Training Camp No. 9

Resource introduction for the 9th session of Niu G...

How to use data for brand marketing?

In this era, data-driven thinking is a quality th...

802.11ad - The Fastest WiFi Solution You've Never Seen

[51CTO.com Quick Translation] Millimeter-wave Wi-...

Sharing of operational experience: 6 ways to increase user activity!

How to maintain and increase activity and improve...