问题现象
Xcode13之前的开发工具编译,选用UIWebView框架的页面能够正常访问,js与原生交互的办法能够正常调用。更新Xcode13后,js提示”Can’t find variable”错误。
解决办法
对第三方库的运用
- 将第三方开源代码导入工程后,对源代码进行简单修正。
- 去除其中的运用的私有api”parentFrame”。
- 不需要在- (BOOL)webView: shouldStartLoadWithRequest:navigationType: 中创立一个新的UIWebView。
#import "UIWebView+TS_JavaScriptContext.h"
#import <JavaScriptCore/JavaScriptCore.h>
#import <objc/runtime.h>
static const char kTSJavaScriptContext[] = "ts_javaScriptContext";
static NSHashTable *g_webViews = nil;
@interface UIWebView (TS_JavaScriptCore_private)
- (void) ts_didCreateJavaScriptContext:(JSContext *)ts_javaScriptContext;
@end
@implementation NSObject (TS_JavaScriptContext)
- (void)webView:(id)unused didCreateJavaScriptContext:(JSContext *)ctx forFrame:(id)frame {
NSLog(@"%s js context 注入模型丢掉问题",__func__);
void (^notifyDidCreateJavaScriptContext)(void) = ^{
for (UIWebView *webView in g_webViews) {
NSString* cookie = [NSString stringWithFormat: @"ts_jscWebView_%lud", (unsigned long)webView.hash ];
[webView stringByEvaluatingJavaScriptFromString: [NSString stringWithFormat: @"var %@ = '%@'", cookie, cookie]];
if ([ctx[cookie].toString isEqualToString:cookie]) {
[webView ts_didCreateJavaScriptContext:ctx];
return;
}
}
};
if ([NSThread isMainThread]) {
notifyDidCreateJavaScriptContext();
} else {
dispatch_async(dispatch_get_main_queue(), ^{
notifyDidCreateJavaScriptContext();
});
}
}
@end
@implementation UIWebView (TS_JavaScriptContext)
+ (id)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
g_webViews = [NSHashTable weakObjectsHashTable];
});
id webView = [super allocWithZone:zone];
[g_webViews addObject:webView];
return webView;
}
- (void)ts_didCreateJavaScriptContext:(JSContext *)ts_javaScriptContext {
NSLog(@"%s runtime 替换办法",__func__);
[self willChangeValueForKey: @"ts_javaScriptContext"];
objc_setAssociatedObject( self, kTSJavaScriptContext, ts_javaScriptContext, OBJC_ASSOCIATION_RETAIN);
[self didChangeValueForKey: @"ts_javaScriptContext"];
if (self.delegate && [self.delegate respondsToSelector:@selector(webView:didCreateJavaScriptContext:)]) {
id<TSWebViewDelegate> delegate = (id<TSWebViewDelegate>)self.delegate;
[delegate webView:self didCreateJavaScriptContext:ts_javaScriptContext];
}
}
- (JSContext *)ts_javaScriptContext {
NSLog(@"%s runtime 获取context",__func__);
JSContext *javaScriptContext = objc_getAssociatedObject(self, kTSJavaScriptContext);
return javaScriptContext;
}
@end
如何运用
- (void)webView:(UIWebView *)webView didCreateJavaScriptContext:(JSContext *)ctx {
self.webView.ts_javaScriptContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
NSLog(@"获取 UIWebView 的 JS 履行环境失败,原因: %@",exception.toString);
};
__weak __typeof(ctx) blockContext = ctx;
__weak __typeof(self) weakSelf = self;
#pragma mark 初始化登录数据
ctx[@"getTokenAndUserAccount"] = ^() {
// 这里是js的回调,把参数回调告知js。
};
}
运用成果
之前能够正常访问页面了,但是随之带来一个新的现象:屡次运行该web控制器后,呈现崩溃。错误消息为
-[xxx respondsToSelector:]message sent to deallocated instance
查询后,触及空指针问题,以及或许某个delegate设置存在问题。
解决
重写控制器。
分析
参考资料中是不触及Xcode13的,个人感觉应该是Xcode13 中缺少了UIWebViewSDK的某些内容。当web页面在load之后,到注册js context的时分,没有注册成功,使JS在原生代码中找不到他需要履行的办法。
现在选用runtime的方式,使得咱们注入模型的机遇在创立 JavaScriptContext
与 JS调用 之间。
问题解决参考资料
UIWebView-TS_JavaScriptContext
iOS奇淫技巧 —— 解决UIWebView重定向后,JSContext 注入的模型丢掉问题