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

到这里就基本结束了。

相关推荐
DisonTangor2 小时前
苹果发布iOS 18.2首个公测版:Siri接入ChatGPT、iPhone 16拍照按钮有用了
ios·chatgpt·iphone
- 羊羊不超越 -2 小时前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
2401_8658548818 小时前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
HackerTom1 天前
iOS用rime且导入自制输入方案
ios·iphone·rime
良技漫谈1 天前
Rust移动开发:Rust在iOS端集成使用介绍
后端·程序人生·ios·rust·objective-c·swift
2401_852403551 天前
高效管理iPhone存储:苹果手机怎么删除相似照片
ios·智能手机·iphone
星际码仔2 天前
【动画图解】是怎样的方法,能被称作是 Flutter Widget 系统的核心?
android·flutter·ios
emperinter2 天前
WordCloudStudio:AI生成模版为您的文字云创意赋能 !
图像处理·人工智能·macos·ios·信息可视化·iphone
关键帧Keyframe2 天前
音视频面试题集锦第 8 期
ios·音视频开发·客户端
pb82 天前
引入最新fluwx2.5.4的时候报错
flutter·ios