首页 > 系统 > iOS > 正文

ios wkwebview离线化加载h5资源解决方案

2019-10-21 18:41:20
字体:
来源:转载
供稿:网友

思路: 使用NSURLProtocol拦截请求转发到本地。

1.确认离线化需求

部门负责的app有一部分使用的线上h5页,长期以来加载略慢...

于是考虑使用离线化加载。

确保[低速网络]或[无网络]可网页秒开。

2.使用[NSURLProtocol]拦截

区别于uiwebview wkwebview使用如下方法拦截

@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {  [super viewDidLoad];  // 区别于uiwebview wkwebview使用如下方法拦截  Class cls = NSClassFromString(@"WKBrowsingContextController");  SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:");  if ([(id)cls respondsToSelector:sel]) {    [(id)cls performSelector:sel withObject:@"http"];    [(id)cls performSelector:sel withObject:@"https"];  }}
# 注册NSURLProtocol拦截- (IBAction)regist:(id)sender {  [NSURLProtocol registerClass:[FilteredProtocol class]];}
# 注销NSURLProtocol拦截- (IBAction)unregist:(id)sender {  [NSURLProtocol unregisterClass:[FilteredProtocol class]];}

3.下载[zip] + 使用[SSZipArchive]解压

需要先 #import "SSZipArchive.h

- (void)downloadZip {  NSDictionary *_headers;  NSURLSession *_session = [self sessionWithHeaders:_headers];  NSURL *url = [NSURL URLWithString: @"http://10.2.138.225:3238/dist.zip"];  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];    // 初始化cachepath  NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];  NSFileManager *fm = [NSFileManager defaultManager];    // 删除之前已有的文件  [fm removeItemAtPath:[cachePath stringByAppendingPathComponent:@"dist.zip"] error:nil];    NSURLSessionDownloadTask *downloadTask=[_session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {    if (!error) {            NSError *saveError;            NSURL *saveUrl = [NSURL fileURLWithPath: [cachePath stringByAppendingPathComponent:@"dist.zip"]];            // location是下载后的临时保存路径,需要将它移动到需要保存的位置      [[NSFileManager defaultManager] copyItemAtURL:location toURL:saveUrl error:&saveError];      if (!saveError) {        NSLog(@"task ok");        if([SSZipArchive unzipFileAtPath:          [cachePath stringByAppendingPathComponent:@"dist.zip"]                  toDestination:cachePath]) {          NSLog(@"unzip ok");// 解压成功        }        else {          NSLog(@"unzip err");// 解压失败        }      }      else {        NSLog(@"task err");      }    }    else {      NSLog(@"error is :%@", error.localizedDescription);    }  }];    [downloadTask resume];}

4.迁移资源至[NSTemporary]

[wkwebview]真机不支持直接加载[NSCache]资源

需要先迁移资源至[NSTemporary]

- (void)migrateDistToTempory {  NSFileManager *fm = [NSFileManager defaultManager];  NSString *cacheFilePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"dist"];  NSString *tmpFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dist"];    // 先删除tempory已有的dist资源  [fm removeItemAtPath:tmpFilePath error:nil];  NSError *saveError;    // 从caches拷贝dist到tempory临时文件夹  [[NSFileManager defaultManager] copyItemAtURL:[NSURL fileURLWithPath:cacheFilePath] toURL:[NSURL fileURLWithPath:tmpFilePath] error:&saveError];  NSLog(@"Migrate dist to tempory ok");}

5.转发请求

如果[/static]开头 => 则转发[Request]到本地[.css/.js]资源

如果[index.html]结尾 => 就直接[Load]本地[index.html] (否则[index.html]可能会加载失败)

//// ProtocolCustom.m// proxy-browser//// Created by melo的微博 on 2018/4/8.// Copyright © 2018年 com. All rights reserved.//#import <objc/runtime.h>#import <Foundation/Foundation.h>#import <MobileCoreServices/MobileCoreServices.h>static NSString*const matchingPrefix = @"http://10.2.138.225:3233/static/";static NSString*const regPrefix = @"http://10.2.138.225:3233";static NSString*const FilteredKey = @"FilteredKey";@interface FilteredProtocol : NSURLProtocol@property (nonatomic, strong) NSMutableData  *responseData;@property (nonatomic, strong) NSURLConnection *connection;@end@implementation FilteredProtocol+ (BOOL)canInitWithRequest:(NSURLRequest *)request{  return [NSURLProtocol propertyForKey:FilteredKey inRequest:request]== nil;}+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request{  NSLog(@"Got it request.URL.absoluteString = %@",request.URL.absoluteString);  NSMutableURLRequest *mutableReqeust = [request mutableCopy];  //截取重定向  if ([request.URL.absoluteString hasPrefix:matchingPrefix])  {    NSURL* proxyURL = [NSURL URLWithString:[FilteredProtocol generateProxyPath: request.URL.absoluteString]];    NSLog(@"Proxy to = %@", proxyURL);    mutableReqeust = [NSMutableURLRequest requestWithURL: proxyURL];  }  return mutableReqeust;}+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b{  return [super requestIsCacheEquivalent:a toRequest:b];}# 如果[index.html]结尾 => 就直接[Load]本地[index.html]- (void)startLoading {  NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];  // 标示改request已经处理过了,防止无限循环  [NSURLProtocol setProperty:@YES forKey:FilteredKey inRequest:mutableReqeust];    if ([self.request.URL.absoluteString hasSuffix:@"index.html"]) {    NSURL *url = self.request.URL;     NSString *path = [FilteredProtocol generateDateReadPath: self.request.URL.absoluteString];        NSLog(@"Read data from path = %@", path);    NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];    NSData *data = [file readDataToEndOfFile];    NSLog(@"Got data = %@", data);    [file closeFile];        //3.拼接响应Response    NSInteger dataLength = data.length;    NSString *mimeType = [self getMIMETypeWithCAPIAtFilePath:path];    NSString *httpVersion = @"HTTP/1.1";    NSHTTPURLResponse *response = nil;        if (dataLength > 0) {      response = [self jointResponseWithData:data dataLength:dataLength mimeType:mimeType requestUrl:url statusCode:200 httpVersion:httpVersion];    } else {      response = [self jointResponseWithData:[@"404" dataUsingEncoding:NSUTF8StringEncoding] dataLength:3 mimeType:mimeType requestUrl:url statusCode:404 httpVersion:httpVersion];    }        //4.响应    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];    [[self client] URLProtocol:self didLoadData:data];    [[self client] URLProtocolDidFinishLoading:self];  }  else {    self.connection = [NSURLConnection connectionWithRequest:mutableReqeust delegate:self];  }}- (void)stopLoading{  if (self.connection != nil)  {    [self.connection cancel];    self.connection = nil;  }}- (NSString *)getMIMETypeWithCAPIAtFilePath:(NSString *)path{  if (![[[NSFileManager alloc] init] fileExistsAtPath:path]) {    return nil;  }    CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[path pathExtension], NULL);  CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);  CFRelease(UTI);  if (!MIMEType) {    return @"application/octet-stream";  }  return (__bridge NSString *)(MIMEType);}#pragma mark - 拼接响应Response- (NSHTTPURLResponse *)jointResponseWithData:(NSData *)data dataLength:(NSInteger)dataLength mimeType:(NSString *)mimeType requestUrl:(NSURL *)requestUrl statusCode:(NSInteger)statusCode httpVersion:(NSString *)httpVersion{  NSDictionary *dict = @{@"Content-type":mimeType,              @"Content-length":[NSString stringWithFormat:@"%ld",dataLength]};  NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:requestUrl statusCode:statusCode HTTPVersion:httpVersion headerFields:dict];  return response;}#pragma mark- NSURLConnectionDelegate- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {  [self.client URLProtocol:self didFailWithError:error];}#pragma mark - NSURLConnectionDataDelegate- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{  self.responseData = [[NSMutableData alloc] init];  [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];}- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {  [self.responseData appendData:data];  [self.client URLProtocol:self didLoadData:data];}- (void)connectionDidFinishLoading:(NSURLConnection *)connection {  [self.client URLProtocolDidFinishLoading:self];}+ (NSString *)generateProxyPath:(NSString *) absoluteURL {  NSString *tmpFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dist"];  NSString *fileAbsoluteURL = [@"file:/" stringByAppendingString:tmpFilePath];  return [absoluteURL stringByReplacingOccurrencesOfString:regPrefix                         withString:fileAbsoluteURL];}+ (NSString *)generateDateReadPath:(NSString *) absoluteURL {  NSString *fileDataReadURL = [NSTemporaryDirectory() stringByAppendingPathComponent:@"dist"];  return [absoluteURL stringByReplacingOccurrencesOfString:regPrefix                         withString:fileDataReadURL];}@end

结语:

完整[DEMO]请参考: https://github.com/meloalright/wk-proxy

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


注:相关教程知识阅读请移步到IOS开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表