// // MOWebBaseView.m // MiMoLive // // Created by SuperC on 2025/9/11. // #import "MOWebBaseView.h" #import "POWeakWKWebViewScriptMessageHandler.h" #import "MOWebViewController.h" #import "MOUploadDeviceTool.h" // 用于标记webView序号 static int webViewTag = 0; @interface MOWebBaseView () /** 标记WebView序号 */ @property (nonatomic, assign) int webViewTag; /**记录是否添加了观察者,避免dealloc时释放观察者崩溃*/ @property (nonatomic, assign) BOOL hasAddedObserver; @end @implementation MOWebBaseView - (instancetype)init { if (self = [super init] ) { [self setupUI]; } return self; } - (instancetype)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self){ [self setupUI]; } return self; } - (void)setupUI{ [self addSubview:self.poWebView]; [self.poWebView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self); }]; } - (void)toLoadTheUrl{ //FIXME: 默认缓存策略是NSURLRequestUseProtocolCachePolicy #if kAPP_Environment == 1 NSURLRequestCachePolicy policy = NSURLRequestUseProtocolCachePolicy; #elif kAPP_Environment == 2 NSURLRequestCachePolicy policy = NSURLRequestUseProtocolCachePolicy; // NSURLRequestCachePolicy policy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData; if (@available(iOS 16.4,*)) { [self.poWebView setInspectable:YES]; } #endif self.request = [NSURLRequest requestWithURL:self.webUrl cachePolicy:policy timeoutInterval:40]; MOLogV(@"[WebView] init poWebView(%d) url:%@",self.webViewTag,self.webUrl); [self.poWebView loadRequest:self.request]; } #pragma mark ========= JS-->OC =============== - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ //#define kWebCallBack_GetUserInfo @"getUserInfo" //传用户信息 if ([message.name isEqualToString:kWebCallBack_GetUserInfo]) { MOMeDataInfo *userInfoData = (MOMeDataInfo *)[[MODataCache sharedYYCache] objectForKey:kMineUserInfo]; if(userInfoData){ // ADJAttribution *adjInfo = [Adjust attribution]; userInfoData.token = GetToken; userInfoData.adid = [MOUploadDeviceTool shareTool].adid; NSString *versionString = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; userInfoData.appVersion = versionString; NSDictionary *userDict = [userInfoData dictionaryRepresentation]; NSString *jsonString = [userDict modelToJSONString]; NSString *jsString = [NSString stringWithFormat:@"%@(%@)",@"getUserInfoResponse",jsonString]; [self.poWebView evaluateJavaScript:jsString completionHandler:nil]; } else{ NSString *jsonString = @"{\"follows\":3,\"vip\":{\"giveGiftBag\":false,\"type\":0,\"expireTime\":0,\"giveGaily\":false},\"profile\":{\"id\":\"65d4ba9f097a721b1ef0a8c5\",\"intro\":\"\",\"medals\":[],\"userNo\":\"43999352\",\"bks\":[],\"agoraId\":43999352,\"adornment\":{\"headdressAnno\":false,\"bubbleCode\":0,\"enterBar\":0},\"avatar\":\"https:\\/\\/mimo-live.s3.ap-southeast-3.amazonaws.com\\/av\\/2024\\/52\\/b78\\/fa5f8c804caae17d1635afa724149118.jpg\",\"birthday\":\"1990-01-01\",\"nickname\":\"SuperY\",\"country\":\"ID\",\"gender\":1},\"mobile\":{},\"family\":{\"memberMax\":0,\"totalPower\":0,\"level\":0,\"memberNum\":0,\"weekPower\":0,\"dailyPower\":0},\"wallet\":{\"diamond\":22,\"consumeDiamond\":0,\"goldenBean\":0},\"fansClub\":{\"show\":false,\"level\":0,\"rank\":0,\"members\":0},\"currExp\":49,\"guild\":{\"anchor\":0},\"level\":3,\"friends\":1,\"fans\":1,\"nextExp\":113,\"liveBadge\":{\"type\":0},\"status\":{\"anchor\":true,\"guild\":true,\"mobile\":false,\"fansClub\":false,\"liveDiscount\":true,\"anchorType\":1,\"joinFansClub\":true}}"; NSString *jsString = [NSString stringWithFormat:@"%@(%@)",@"getUserInfoResponse",jsonString]; [self.poWebView evaluateJavaScript:jsString completionHandler:nil]; } } //#define kWebCallBack_GetLanguage @"getLanguage" //语言 else if ([message.name isEqualToString:kWebCallBack_GetLanguage]){ NSString *currentLanguage = [NSBundle currentLanguage]; if(currentLanguage.length == 0){ currentLanguage = @"en-us"; } NSDictionary *userDict = @{@"language":currentLanguage}; NSString *jsonString = [userDict modelToJSONString]; NSString *jsString = [NSString stringWithFormat:@"%@(%@)",@"getLanguageResponse",jsonString]; [self.poWebView evaluateJavaScript:jsString completionHandler:nil]; } //#define kWebCallBack_GetStatusHeight @"getStatusHeight" //状态栏高度 else if ([message.name isEqualToString:kWebCallBack_GetStatusHeight]){ CGFloat statusHeght = STATUS_BAR_HEIGHT + 44; NSDictionary *userDict = @{@"statusHeight":@(statusHeght)}; NSString *jsonString = [userDict modelToJSONString]; NSString *jsString = [NSString stringWithFormat:@"%@(%@)",@"getStatusHeightResponse",jsonString]; [self.poWebView evaluateJavaScript:jsString completionHandler:nil]; } else if ([message.name isEqualToString:@"destroy"]){ } //#define kWebCallBack_Close @"closeActivity" //关闭界面 else if ([message.name isEqualToString:kWebCallBack_Close]){ } //#define kWebCallBack_ExportUrl @"exportUrl" //导出链接 else if ([message.name isEqualToString:kWebCallBack_ExportUrl]){ [self toExportUrlWith:message.body]; return; } } /* 页面开始加载 */ - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{ NSString *urlString = webView.URL.absoluteString; MOLogV(@"Navigated to URL: %@", urlString); } - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{ NSString *urlString = webView.URL.absoluteString; MOLogV(@"Navigated to URL: %@", urlString); } - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{ // 在收到响应开始加载后,决定是否跳转 NSString *urlString = webView.URL.absoluteString; MOLogV(@"Navigated to URL: %@", urlString); decisionHandler(WKNavigationResponsePolicyAllow); } /// 页面加载失败,指初始化第一个页面 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(reloadWebViewData) object:nil]; [self performSelector:@selector(reloadWebViewData) withObject:nil afterDelay:2.0]; } - (void)reloadWebViewData{ #if kAPP_Environment == 1 NSURLRequestCachePolicy policy = NSURLRequestUseProtocolCachePolicy; #elif kAPP_Environment == 2 NSURLRequestCachePolicy policy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData; #endif self.request = [NSURLRequest requestWithURL:self.webUrl cachePolicy:policy timeoutInterval:40]; MOLogV(@"[WebView] init poWebView(%d) url:%@",self.webViewTag,self.webUrl); [self.poWebView loadRequest:self.request]; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"title"]) { if (object == self.poWebView) { } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } /* 在发送请求之前,决定是否跳转 */ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ NSString *urlString = navigationAction.request.URL.absoluteString; MOLogV(@"mimo-urlString-%@",urlString); if([urlString containsString:@"mimo://"]){ NSURL *url = [NSURL URLWithString:urlString]; MOLogV(@"mimo-urlString-%@",urlString); if ([[UIApplication sharedApplication]respondsToSelector:@selector(openURL:options:completionHandler:)]) { [[UIApplication sharedApplication] openURL:url options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@NO} completionHandler:^(BOOL success) {}]; } else { [[UIApplication sharedApplication]openURL:webView.URL]; } decisionHandler(WKNavigationActionPolicyCancel); return; } MOLogV(@"12313"); decisionHandler(WKNavigationActionPolicyAllow); } - (void)toExportUrlWith:(NSString *)jsonStr{ NSDictionary *dict = [self dictionaryWithJsonString:jsonStr]; NSString *theUrl = [MODataManager objectOrNilForKey:@"url" fromDictionary:dict[@"data"]]; NSURL *url = [NSURL URLWithString:theUrl]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; } } - (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString{ if(jsonString == nil && jsonString.length == 0){ return nil; } NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; NSError *err; NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err]; if(err){ MOLogV(@"json解析失败:%@",err); return nil; } return dic; } #pragma mark - Lazy - (WKWebView *)poWebView { if (!_poWebView) { WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; config.selectionGranularity = WKSelectionGranularityDynamic; config.allowsInlineMediaPlayback = YES; config.mediaTypesRequiringUserActionForPlayback = NO; WKPreferences *preferences = [WKPreferences new]; //是否支持JavaScript preferences.javaScriptEnabled = YES; //不通过用户交互,是否可以打开窗口 preferences.javaScriptCanOpenWindowsAutomatically = YES; config.preferences = preferences; [config.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"]; [config setValue:@YES forKey:@"allowUniversalAccessFromFileURLs"]; //新建一个弱引用代理,防止循环引用 POWeakWKWebViewScriptMessageHandler *weakHandler = [[POWeakWKWebViewScriptMessageHandler alloc] initWithSriptMessageHandler:self]; config.userContentController = [self userControllerWithMessageHandler:weakHandler]; _poWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, SCREENWIDTH, SCREENHEIGHT) configuration:config]; [_poWebView setBackgroundColor:[UIColor whiteColor]]; [_poWebView setOpaque:NO]; WEAKSELF; [_poWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable result, NSError * _Nullable error) { NSString *oldUA = result; NSString *newUA = [NSString stringWithFormat:@"%@/xiaohui",oldUA]; weakSelf.poWebView.customUserAgent = newUA; }]; _poWebView.navigationDelegate = self; _poWebView.UIDelegate = self; [_poWebView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL]; self.webViewTag = webViewTag; webViewTag++; self.hasAddedObserver = YES; _poWebView.scrollView.bounces = NO;//禁用弹性效果 } return _poWebView; } - (WKUserContentController *)userControllerWithMessageHandler:(POWeakWKWebViewScriptMessageHandler *)weakHandler{ WKUserContentController *userController = [[WKUserContentController alloc] init]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_Close]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_GetUserInfo]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_GetStatusHeight]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_GetLanguage]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_GetDiamond]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_ToGetRain]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_ToSubmitShareLink]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_TokenExpired]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_ExportUrl]; // [userController addScriptMessageHandler:weakHandler name:@"recharge"]; [userController addScriptMessageHandler:weakHandler name:@"clickRecharge"]; [userController addScriptMessageHandler:weakHandler name:@"newTppClose"]; // 2 [userController addScriptMessageHandler:weakHandler name:@"getConfig"]; [userController addScriptMessageHandler:weakHandler name:@"destroy"]; [userController addScriptMessageHandler:weakHandler name:[MOTextAESTools toGetHameRecharge]]; [userController addScriptMessageHandler:weakHandler name:[MOTextAESTools toGetHameLoaded]]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_ShareToPlatAgency]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_BindAgencySuccess]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_ChagneMysteriousStatus]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_GetBlindBoxScreenParams]; [userController addScriptMessageHandler:weakHandler name:kWebCallBack_SetBlindBoxResult]; return userController; } - (void)removeHandler{ [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_Close]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_GetUserInfo]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_GetStatusHeight]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_GetLanguage]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_GetDiamond]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_ToGetRain]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_ToSubmitShareLink]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_TokenExpired]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_ExportUrl]; // [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:@"recharge"]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:@"clickRecharge"]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:@"newTppClose"]; // 2 [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:@"getConfig"]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:@"destroy"]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:[MOTextAESTools toGetHameRecharge]]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:[MOTextAESTools toGetHameLoaded]]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_ShareToPlatAgency]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_BindAgencySuccess]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_ChagneMysteriousStatus]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_GetBlindBoxScreenParams]; [_poWebView.configuration.userContentController removeScriptMessageHandlerForName:kWebCallBack_SetBlindBoxResult]; } - (void)dealloc{ [self removeHandler]; } @end