Speed ​​up WKWebView loading

As explained here , WKWebView has an error in which applications that link to a local web page must copy the package to the tmp . My code for copying a package to tmp :

 // Clear tmp directory NSArray* temporaryDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:NSTemporaryDirectory() error:NULL]; for (NSString *file in temporaryDirectory) { [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), file] error:NULL]; } NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"build"]; NSString *temporaryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"build"]; NSError *error = nil; // Copy directory if(![fileManager copyItemAtPath:sourcePath toPath:temporaryPath error:&error]) { [self logError:@"Could not copy directory" :error]; } 

I timed this piece of code, and it takes 50% of the time to launch my application! (~ 0.5 s for a total of ~ 1 s.)

Is there a way to speed up (or completely eliminate) this piece of code under iOS 8? Did the thread help?

+5
source share
4 answers

Given that your assets are large enough to get copied 0.5 seconds, I would suggest a slightly different approach to speed up the (obvious) startup time:

  • Create a plain old HTML page with a placeholder div for loading assets (if you want, you can include small objects (for example, loading indicator images) in base64)
  • Once this HTML loads (possibly on webView:didFinishNavigation: , run the copy in the build folder
  • Once the copy is complete, fill in the placeholders with your assets using javascript (with evaluateJavaScript:completionHandler:

Although I understand that this does not directly address the issue, I think that in this case it would be better to change the approach.

+5
source

NOTE. This method works well with UIKit UIWebView and AppKit WebView , but does not work for the new WKWebView , which seems to ignore the URL loading system. See Comments.

Use NSURLProtocol to process local files as if they were remote requests. For a complete example, see PandoraBoy called ResourceURLProtocol . I will go through a slightly simplified version of this here. We will read http://.RESOURCE./path/to/file as if it were <resources>/path/to/file .

Each NSURLProtocol will be asked if it can handle every request that appears on the system. He should answer if it could be in +canInitWithRequest: We will say if the host is .RESOURCE. then we can process it. .RESOURCE. is an illegal DNS name, so it cannot conflict with any real host (but it is a legitimate host name for the purpose of the URL).

 NSString *ResourceHost = @".RESOURCE."; + (BOOL)canInitWithRequest:(NSURLRequest *)request { return ( [[[request URL] scheme] isEqualToString:@"http"] && [[[request URL] host] isEqualToString:ResourceHost] ); } 

Then we need several accounting methods. Nothing is visible here.

 + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { return request; } -(void)stopLoading { return; } 

Now we get to the meat. startLoading is where you are going to do whatever you want to do with the request.

 -(void)startLoading { NSBundle *thisBundle = [NSBundle bundleForClass:[self class]]; NSString *notifierPath = [[thisBundle resourcePath] stringByAppendingPathComponent:[[[self request] URL] path]]; NSError *err; NSData *data = [NSData dataWithContentsOfFile:notifierPath options:NSUncachedRead // Assuming you only need to read this once error:&err]; if( data != nil ) { // Assuming you're only reading HTML. // If you need other things, you'll need to work out the correct MIME type NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[[self request] URL] MIMEType:@"text/html" expectedContentLength:[data length] textEncodingName:nil]; // And we just pass it to the caller [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed]; [[self client] URLProtocol:self didLoadData:data]; [[self client] URLProtocolDidFinishLoading:self]; } else { NSLog(@"BUG:Unable to load resource:%@:%@", notifierPath, [err description]); [[self client] URLProtocol:self didFailWithError:err]; } } 

I also found a bit of ResourceURL wrapper:

 @implementation ResourceURL + (ResourceURL*) resourceURLWithPath:(NSString *)path { return [[[NSURL alloc] initWithScheme:@"http" host:ResourceHost path:path] autorelease]; } @end 

To use it, you just need to register the protocol handler first:

 [NSURLProtocol registerClass:[ResourceURLProtocol class]]; 

Then you can create a β€œresource URL” and load it:

 ResourceURL *resource = [ResourceURL resourceURLWithPath:...]; [webView loadRequest:[NSURLRequest requestWithURL:resource]]; 

For more information on NSURLProtocol , as well as a more sophisticated caching example, see Rollback Offline Caching for UIWebView (and NSURLProtocol) .

PandoraBoy is full of NSURLProtocol examples (look for all classes with Protocol in your names). You can capture, track, redirect or manipulate almost everything that happens through the URL loading system in this way.

+2
source

Try XWebView without copying files. It includes a tiny http server for you. You just need to call [webview loadFileURL: allowReadAccessToURL:]

+1
source

Performing this parallel with the remainder of 0.5 from launch will definitely help. (This would be possible only if what you do in the rest of the initial start up does not depend on the loaded WebView or the files read)

The best optimization you get is to reduce the size of the files inside the build directory. Try it -

+1
source

All Articles