mop-flutter-sdk/ios/Classes/FinAppletExt/View/FATWebView.m

261 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

//
// FATWebView.m
// FinApplet
//
// Created by Haley on 2019/12/9.
// Copyright © 2019 finogeeks. All rights reserved.
//
#import "FATWebView.h"
#import <FinApplet/FinApplet.h>
#import <WebKit/WebKit.h>
#import "FATExtUtil.h"
@interface FATWebView () <WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate, UIScrollViewDelegate>
@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) UIProgressView *progressView;
@property (nonatomic, copy) NSString *appletId;
@end
@implementation FATWebView
- (instancetype)initWithFrame:(CGRect)frame URL:(NSURL *)URL appletId:(NSString *)appletId {
self = [super initWithFrame:frame];
if (self) {
_appletId = appletId;
[self p_initSubViews:URL];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.webView.frame = self.bounds;
CGFloat y = fabs(self.webView.scrollView.contentOffset.y);
self.progressView.frame = CGRectMake(0, y, self.bounds.size.width, 4);
}
- (void)dealloc {
[self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
}
#pragma mark - private method
- (void)p_initSubViews:(NSURL *)URL {
FATWeakScriptMessageDelegate *scriptMessageDelegate = [FATWeakScriptMessageDelegate new];
scriptMessageDelegate.scriptDelegate = self;
WKUserContentController *userContentController = [WKUserContentController new];
NSString *souce = @"window.__fcjs_environment='miniprogram'";
WKUserScript *script = [[WKUserScript alloc] initWithSource:souce injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:true];
[userContentController addUserScript:script];
[userContentController addScriptMessageHandler:scriptMessageDelegate name:@"webInvokeHandler"];
[userContentController addScriptMessageHandler:scriptMessageDelegate name:@"webPublishHandler"];
WKWebViewConfiguration *wkWebViewConfiguration = [WKWebViewConfiguration new];
wkWebViewConfiguration.allowsInlineMediaPlayback = YES;
wkWebViewConfiguration.userContentController = userContentController;
self.webView = [[WKWebView alloc] initWithFrame:self.bounds configuration:wkWebViewConfiguration];
self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
self.webView.clipsToBounds = YES;
self.webView.scrollView.delegate = self;
[self addSubview:self.webView];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:60];
[self.webView loadRequest:request];
self.progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, 4)];
self.progressView.progressTintColor = [UIColor colorWithRed:44 / 255.0 green:127 / 255.0 blue:251 / 255.0 alpha:1];
[self addSubview:self.progressView];
[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
NSString *version = [FATClient sharedClient].version;
NSString *appendUserAgent;
NSString *model = [[UIDevice currentDevice] model];
if ([FATExtUtil currentProductIdentificationIsEmpty]) {
appendUserAgent = [NSString stringWithFormat:@"Provider/finogeeks (%@; miniprogram; FinChat; runtimeSdkVersion/%@)", model, version];
} else {
appendUserAgent = [NSString stringWithFormat:@"Provider/%@ (%@; miniprogram; %@; runtimeSdkVersion/%@)", [FATExtUtil currentProductIdentification], model, [FATExtUtil currentProductIdentification], version];
}
NSString *customUA = [FATClient sharedClient].uiConfig.appendingCustomUserAgent;
if (customUA.length > 0) {
appendUserAgent = [appendUserAgent stringByAppendingString:@" "];
appendUserAgent = [appendUserAgent stringByAppendingString:customUA];
}
__weak typeof(self) weakSelf = self;
[self.webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable result, NSError *_Nullable error) {
NSString *userAgent = result;
userAgent = [userAgent stringByAppendingFormat:@" %@", appendUserAgent];
weakSelf.webView.customUserAgent = userAgent;
}];
}
- (void)callJS:(NSString *)js callback:(void (^)(id result, NSError *error))callback {
[self.webView evaluateJavaScript:js completionHandler:^(id _Nullable result, NSError *_Nullable error) {
if (callback) {
callback(result, error);
}
}];
}
- (void)webInvokeHandler:(NSDictionary *)data {
if (!data) {
return;
}
NSString *command = data[@"C"];
NSString *paramsString = data[@"paramsString"];
// 可能是字符串也可能是number
id callbackId = data[@"callbackId"];
if (!command) {
return;
}
if ([command isEqualToString:@"initPage"]) {
unsigned long long webPageId = self.webView.hash;
NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.webInvokeCallbackHandler('%@',%@)", callbackId, @(webPageId)];
[self callJS:js callback:nil];
return;
}
// 执行注入的事件
NSDictionary<NSString *, FATWebExtensionApiHandlerModel *> *webExtensionApis = [FATWebExtension webExtensionApis];
id handler = (__bridge id)(webExtensionApis[command].isOld ? webExtensionApis[command].deprecatedHandler : webExtensionApis[command].handler);
if (handler) {
FATExtensionApiCallback callbck = ^void(FATExtensionCode code, NSDictionary<NSString *, NSObject *> *result) {
NSString *successErrMsg = [NSString stringWithFormat:@"%@:ok", command];
NSString *failErrMsg = [NSString stringWithFormat:@"%@:fail", command];
NSString *cancelErrMsg = [NSString stringWithFormat:@"%@:cancel", command];
NSString *errMsg = (NSString *)result[@"errMsg"];
if (errMsg && [errMsg isKindOfClass:[NSString class]] && errMsg.length > 0) {
successErrMsg = [successErrMsg stringByAppendingFormat:@" %@", errMsg];
failErrMsg = [failErrMsg stringByAppendingFormat:@" %@", errMsg];
cancelErrMsg = [cancelErrMsg stringByAppendingFormat:@" %@", errMsg];
}
switch (code) {
case FATExtensionCodeSuccess: {
NSMutableDictionary *successResult = [NSMutableDictionary dictionaryWithDictionary:result];
[successResult setObject:successErrMsg forKey:@"errMsg"];
NSString *resultJsonString = [self fat_jsonStringFromDict:successResult];
NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.webInvokeCallbackHandler('%@',%@)", callbackId, resultJsonString];
[self callJS:js callback:nil];
break;
}
case FATExtensionCodeCancel: {
NSMutableDictionary *cancelResult = [NSMutableDictionary dictionaryWithDictionary:result];
[cancelResult setObject:cancelErrMsg forKey:@"errMsg"];
NSString *resultJsonString = [self fat_jsonStringFromDict:cancelResult];
NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.webInvokeCallbackHandler('%@',%@)", callbackId, resultJsonString];
[self callJS:js callback:nil];
break;
}
case FATExtensionCodeFailure: {
NSMutableDictionary *failResult = [NSMutableDictionary dictionaryWithDictionary:result];
[failResult setObject:failErrMsg forKey:@"errMsg"];
NSString *resultJsonString = [self fat_jsonStringFromDict:failResult];
NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.webInvokeCallbackHandler('%@',%@)", callbackId, resultJsonString];
[self callJS:js callback:nil];
break;
}
default:
break;
}
};
NSDictionary *param = [self fat_jsonObjectFromString:paramsString];
if (webExtensionApis[command].isOld) {
webExtensionApis[command].deprecatedHandler(param, callbck);
} else {
FATAppletInfo *appletInfo = [[FATClient sharedClient] getAppletInfo:self.appletId];
webExtensionApis[command].handler(appletInfo, param, callbck);
}
return;
}
NSMutableDictionary *failResult = [NSMutableDictionary dictionary];
NSString *failErrMsg = [NSString stringWithFormat:@"%@:fail 该api未实现", command];
[failResult setObject:failErrMsg forKey:@"errMsg"];
NSString *resultJsonString = [self fat_jsonStringFromDict:failResult];
NSString *js = [NSString stringWithFormat:@"FinChatJSBridge.webInvokeCallbackHandler('%@',%@)", callbackId, resultJsonString];
[self callJS:js callback:nil];
}
- (void)webPublishHandler:(NSDictionary *)data {
}
#pragma mark - KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
[self.progressView setAlpha:1.0f];
[self.progressView setProgress:self.webView.estimatedProgress animated:YES];
if (self.webView.estimatedProgress >= 1.0f) {
[UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.progressView setAlpha:0.0f];
} completion:^(BOOL finished) {
[self.progressView setProgress:0.0f animated:YES];
}];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
//MARK: - TOOL METHOD
- (NSString *)fat_jsonStringFromDict:(NSDictionary *)dict {
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
if (!data) {
return nil;
}
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return jsonString;
}
- (id)fat_jsonObjectFromString:(NSString *)string {
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
if (!data) {
return nil;
}
id object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
return object;
}
//MARK: - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSString *name = message.name;
id body = message.body;
if ([name isEqualToString:@"webInvokeHandler"]) {
[self webInvokeHandler:body];
} else if ([name isEqualToString:@"webPublishHandler"]) {
[self webPublishHandler:body];
}
}
//MARK: - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
decisionHandler(WKNavigationActionPolicyAllow);
}
@end