iOS WKWebView中网络请求拦截(拦截Web中的文件上传接口和文件,然后进行本地后台上传)

需求说明:

一个web页面,需要嵌入到App进行文件上传,但是App在一段时间就会息屏,息屏类似于进入后台,会停止App的一些活动。上传大型文件时需要很长时间,经常息屏,所以看下App能不能实现在不改动web代码的前提下让web在后台上传文件。

问题解决的思路。

  1. 首先是让App进入后台不被挂起(杀死),可以在后台进行网络请求。
  2. 拦截需要上传的文件,再拦截web的文件上传的链接(网络请求拦截),App在本地进行文件上传,上传成功之后告诉网页上传成功了。
  3. 补充方法,让屏幕保持常亮[[UIApplication sharedApplication] setIdleTimerDisabled: YES]不过要记得在dealloc中设置为 NO.

在iOS16下,即使App一直在后台存活,WKWebView会停止网络请求,所以必须进行拦截,然后App进行文件上传操作,然后及时返回给网页,这样就可以保证WKWebView的网络请求存活.

在iOS17beta3中,不进行拦截的情况下,只要保证App在后台存活,WKWebView可以进行网络请求。后续再iOS17正式版发布之后在进行测试是否真的支持。

拦截后文件上传有两种方案,

方案一

由于拦截到的文件formData丢失,但是可以拦截到上传的 range,也就是文件范围,App对文件进行分段,然后自己组装formData数据进行实时上传。

截取部分文件代码

ini 复制代码
NSFileHandle * readFileHandle;
//offset偏移量  size长度
[readFileHandle seekToFileOffset:offset];
NSData * fileFragmentData = [fileModel.readFileHandle readDataOfLength:size];

方案二 :

App通过AFNetworking进行文件上传,并通知记录上传进度,在拦截到网页的接口时获取网页当前进度,如果App进度超过网页进度,那么直接返回网页,告诉他当前已经上传成功,形成一个"伪同步"。

例如一个文件10M,通过表单分成10段上传, App如果网速很快,1秒上传了8M, 那么在拦截的网页中,前八次请求都返回成功。 第九次上传,进入隧道,App信号不好了。上传了20秒,那么拦截到web第九次请求就一直等待,直到App请求成功,在进行web接口的响应。

| App | web |
|--------|-----|---------------|
| 5%-10% | 1% | 那么1-5直接告诉他成功, |

需要解决的技术点

问题一:如何让App在一直在后台存活。

解决:通过让App在后台播放无声音乐的方式可以做到让App理论上的无限存活。 问题二:怎么拦截要上传的文件。 解决:使用Runtime方法交换,Hook到上传的文件。 问题三:怎么拦截文件上传链接(网络请求)。 解决:使用WKURLSchemeHandler

下面详细介绍这些问题的解决过程。

一、如何让App在一直在后台存活。

iOS7以后提供的后台接口模式

1、Background Audio,这是后台的音频,类似于各种音乐播放器。

2、Location Services,这是后台的定位,类似于各种地图导航应用。

3、VoIP,后台语音服务,类似于各种聊天软件。

4、Newsstand,报刊杂志后台自动下载更新,其能够自动实时更新。

我选择的是第一种,可以做到无感知。简而言之就是在后台循环播放一段无声地音乐。

代码:

objectivec 复制代码
/// 创建音乐播放器
- (**void**)creatAVAudioSessionObject{
   //设置后台模式和锁屏模式下依然能够播放
   [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:**nil**];
   [[AVAudioSession sharedInstance] setActive: **YES** error: **nil**];
   //初始化音频播放器
   NSError *playerError;
   NSURL *urlSound = [[NSURL alloc]initWithString:[[NSBundle mainBundle]pathForResource:@"laojie" ofType:@"mp3"]];
   _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:urlSound error:&playerError];
   _audioPlayer.numberOfLoops = -1;//无限播放
   _audioPlayer.volume = 0;
}
/// 开始播放声音
- (**void**)startPlayAudioSession{
   **BOOL**  isPlay = [_audioPlayer play];
    NSLog(@"isPlay===%id", isPlay);
}
/// 停止播放声音
- (**void**)stopPlayAudioSession{
   [_audioPlayer stop];
}

使用方式。

首先,打开后台播放音乐开关。

1.在didFinishLaunchingWithOptions中 调用 creatAVAudioSessionObject

2.在 applicationDidEnterBackground中 调用 startPlayAudioSession

3.在 applicationWillEnterForeground中 调用 stopPlayAudioSession

进入后台时,可以写一个倒计时,通过下面代码后台剩余活跃时间。
[UIApplication sharedApplication].backgroundTimeRemaining

怎么拦截(Hook)要上传的文件

添加分类方法,在内部通过Runtime更换掉系统的获取文件的方法。从而拦截从文件系统/相机相册获取的文件。

具体代码看文件:
拦截UIImagePickerController
拦截UIDocumentPickerViewController
拦截PHPickerViewController(iOS14之后从文件管理里面获取文件使用这个)。

使用方式:

1、在viewDidLoadhookDelegate 例如 [UIImagePickerController hookDelegate] 2、在deallocunHookDelegate

由于文件过大,所以文件管理使用了 NSFileHandle

怎么拦截文件上传链接(网络请求)

iOS11之后系统提供了:WKURLSchemeHandler来拦截WKWenView中的网络请求。

首先创建一个对象遵守WKURLSchemeHandler代理,用于进行请求的拦截处理。
.h代码查看
.m代码查看

使用方式

ini 复制代码
WKWebViewConfiguration * con = [[WKWebViewConfiguration alloc] init];
con.allowsInlineMediaPlayback = YES;
con.allowsPictureInPictureMediaPlayback = YES;
GSKHFURLSchemeHandler *Scheme = [GSKHFURLSchemeHandler new];
[con setURLSchemeHandler:Scheme forURLScheme:@"https"];
_webview = [[WKWebView alloc] initWithFrame:CGRectZero configuration:con];

这里拦截 https 会 crash,因为系统不支持,所以还需要做一下处理。

给WkWebView添加一个分类,在分类里面通过Runtime,把handlesURLScheme替换成自己的方法,对拦截的https进行单独的处理。

代码如下:

less 复制代码
#import "WKWebView+SchemeHandle.h"
#import <objc/runtime.h>
 
@implementation WKWebView (SchemeHandle)
 
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method originalMethod1 = class_getClassMethod(self, @selector(handlesURLScheme:));
        Method swizzledMethod1 = class_getClassMethod(self, @selector(yyhandlesURLScheme:));
        method_exchangeImplementations(originalMethod1, swizzledMethod1);
    });
}

+ (BOOL)yyhandlesURLScheme:(NSString *)urlScheme {
    if ([urlScheme isEqualToString:@"http"] || [urlScheme isEqualToString:@"https"] || [urlScheme isEqualToString:@"file"]) {
        return NO;  //这里让返回NO,应该是默认不走系统断言或者其他判断啥的
    } else {
        return [self handlesURLScheme:urlScheme];
    }
}
@end

到这里就基本结束了。

相关推荐
lichao89042741 分钟前
谈谈未来iOS越狱或巨魔是否会消失
ios
fzxwl4 小时前
隆重推荐(Android 和 iOS)UI 自动化工具—Maestro
android·ui·ios
若水无华2 天前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
Aress"2 天前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy2 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克2 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨3 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆3 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂3 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T4 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa