当前位置: 移动技术网 > IT编程>移动开发>IOS > ios wkwebview离线化加载h5资源解决方案

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

2019年07月24日  | 移动技术网IT编程  | 我要评论

法国巴黎天气,女欢女爱第二季,飞猪旅行推荐托迈酷客

思路: 使用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]资源

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

//
// 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];
}
# 如果[]结尾 => 就直接[load]本地[]
- (void)startloading {
  nsmutableurlrequest *mutablereqeust = [[self request] mutablecopy];
  // 标示改request已经处理过了,防止无限循环
  [nsurlprotocol setproperty:@yes forkey:filteredkey inrequest:mutablereqeust];
  
  if ([self.request.url.absolutestring hassuffix:@""]) {

    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]请参考:

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网