美文网首页
webview转wkwebview遇到的问题

webview转wkwebview遇到的问题

作者: zttjhm | 来源:发表于2019-01-03 16:03 被阅读0次

    webview转wkwebview遇到的坑

    1、wkwebview里通过新窗品进行ajax的post请求时,cookie参数丢失

    解决方案:不创建新wkwebview

    2、原生网络请求请求后cookie无法同步到wkwebview

    @implementation WKWebView (cookieMgr)

    - (void)syncCookies {

        NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];

        if (@available(iOS 11.0, *)) {

            WKHTTPCookieStore *cookieStroe = self.configuration.websiteDataStore.httpCookieStore;

            if (cookies.count == 0) {

                return;

            }

            for (NSHTTPCookie *cookie in cookies) {

                [cookieStroe setCookie:cookie completionHandler:^{

                    if ([[cookies lastObject] isEqual:cookie]) {

                        return;

                    }

                }];

            }

        }else {

            for (NSHTTPCookie *ck in cookies) {

                NSString *script = [NSString stringWithFormat:@"document.cookie='%@=%@;domain=%@;path=%@; expiresDate=\"%@\";isSecure=%@;sessionOnly=%@'",ck.name,ck.value,ck.domain,(ck.path?:@"/"),ck.expiresDate,(ck.isSecure ? @"TRUE":@"FALSE"),(ck.sessionOnly?@"TRUE":@"FALSE")];

                [self evaluateJavaScript:script completionHandler:nil];

            }

        }

    }

    @end

    3、退出登录时,再次登录,部分页面cookie错误

    在退出登录时,如果将WKProcessPool单例对象重置,会造成以前打开页面和新打开cookie错误现象

    4、关于原生方法调用的问题

    webview采用jsexport为原生导出相应方法

    wk通过js注入,创建原生对应对象并调用

    JS代码示例:

    window.uz$q = {

        c:[],

        flag:true,

    };

    window.uz$cb = {

        fn:{},

        id:1,

        on:function(cbId, ret, err, del) {

            if (this.fn[cbId]) {

                this.fn[cbId](ret, err);

                if (del) {

                    delete this.fn[cbId];

                }

            }

        }

    };

    function _onResultCallback(cbId, ret, err, del) {

        return function(){

            uz$cb.on(cbId, ret, err, del);

        }

    }

    function onResultCallback(cbId, ret, err, del) {

        setTimeout(_onResultCallback(cbId, ret, err, del), 0);

    };

    window.uz$md = {};

    function uz$e(c, m, p, isSync, module){

        param = {};

        param.cbId = -1;    //-1 表示没有回调函数

        newP = [];

        var str = Object.prototype.toString.call(p);

        if (str == "[object Arguments]" && p.length > 0) {

            p = Array.from(p);

            for (var index = 0; index < p.length;index ++) {

                node = p[index];

                str = Object.prototype.toString.call(node);

                if (str == "[object Function]") {

                    param.cbId = uz$cb.id ++;

                    uz$cb.fn[param.cbId] = node;

                    newP.push(str);

                }else {

                    newP.push(node);

                }

            }

        }

        param.param = newP;

        /*

        if (p.length === 1) {

            var p0 = p[0];

    if (Object.prototype.toString.call(p0) === "[object Object]") {

                param = p0;

            } else if (typeof p0 === "function") {

                param.cbId = uz$cb.id++;

                uz$cb.fn[param.cbId] = p0;

            }

        } else if (p.length === 2) {

            var p0 = p[0];

            var p1 = p[1];

            if (Object.prototype.toString.call(p0) === "[object Object]") {

                param = p0;

            }

            if (typeof p1 === "function") {

                param.cbId = uz$cb.id++;

                uz$cb.fn[param.cbId] = p1;

            }

    }

        */

        var message = {};

        message.class = c;

        message.method = m;

        message.param = param;

        message.isSync =  false;

        message.module = module;

        window.webkit.messageHandlers.nativeAndroidOrIos.postMessage(message);

    };

    function uz$shift() {

        if (uz$q.c.length > 0) {

            uz$q.c.shift();

        }

        uz$q.flag = true;

    };

    $object_define_placeholder$; //对象定义点位符

    nativeAndroidOrIos.require = function(name, cb) {

        var module = uz$md[name];

        if (!module && nativeAndroidOrIos.useJavaScriptCore) {

            var moduleInfo = nativeAndroidOrIos.getModule({name:name, sync:true});

            if (moduleInfo) {

                var name = moduleInfo.name;

                var className = moduleInfo.class;

                var methods = moduleInfo.methods;

                var syncMethods = moduleInfo.syncMethods;

                uz$md[name] = {};

                if (methods && Object.prototype.toString.call(methods) == '[object Array]') {

                    for (var i=0;i<methods.length;i++) {

                        (function() {

                            var method = methods[i];

                            uz$md[name][method] = function() {

                                return uz$e(className, method, arguments, false, name);

                            }

                        })();

                    }

                }

                if (syncMethods && Object.prototype.toString.call(syncMethods) == '[object Array]') {

                    for (var i=0;i<syncMethods.length;i++) {

                        (function() {

                            var method = syncMethods[i];

                            uz$md[name][method] = function() {

                                return uz$e(className, method, arguments, true, name);

                            }

                        })();

                    }

                }

                module = uz$md[name];

            }

        }

        if (module) {

            return module;

        } else {

            if (cb) {

                cb('undefined', {code:1,msg:name+' module not found'});

            } else {

                return null;

            }

        }

    }

    $method_define_placeholder$; //函数定义占位符

    //var uzmeta = window.document.getElementsByTagName("meta");

    //for(var i=0;i<uzmeta.length;i++){

    //    var name = uzmeta[i].getAttribute('name');

    //    var content = uzmeta[i].getAttribute('content');

    //    if (name && name=='viewport' && content){

    //        content = content.replace(/width=\d{1,}/,'width=device-width');

    //        content = content.replace('width=device-width','width=device-width');

    //        uzmeta[i].setAttribute('content',content);

    //    }

    //}

    //var uzmeta = window.document.getElementsByTagName("meta");

    //for(var i=0;i<uzmeta.length;i++) {

    //    var name = uzmeta[i].getAttribute('name');

    //    var content = uzmeta[i].getAttribute('content');

    //    if (name && name=='viewport' && content){

    //        content = content+',user-scalable=0';

    //        uzmeta[i].setAttribute('content',content);

    //    }

    //}

    document.documentElement.style.webkitTouchCallout = 'none';

    document.documentElement.style.webkitUserSelect = 'none';

    /*

    window.alert = function(arg) {

        var msg;

        if (arg === null) {

            msg = 'null';

        } else if (arg === undefined) {

            msg = 'undefined';

        } else {

            msg = arg.toString();

        }

        nativeAndroidOrIos.alert({

            title:'',

            msg:msg,

            buttons:['好']

        });

    }

    */

    var originalFunc_onerror = window.onerror;

    window.onerror = function(message, url, line) {

        var param = {};

        param.message = message;

        param.url = url;

        param.line = line;

        originalFunc_onerror.apply(window, arguments);

    }

    function getContentFromArg(arg){

        var content;

        var args = Array.prototype.slice.call(arg);

        if (args.length >= 1) {

            if (args[0] === null) {

                args[0] = 'null';

            }

            if (args[0] === undefined) {

                args[0] = 'undefined';

            }

            args[0] = args[0].toString();

        }

        if (args.length > 1) {

            var i = 1;

            if (args[0].indexOf('%c') == 0) {

                args[0] = args[0].replace(/%c/,'');

                i = 2;

            }

            for (; i<args.length; i++) {

                if (/%s|%d|%i|%o/.test(args[0])) {

                    args[0] = args[0].replace(/%s|%d|%i|%o/, args[i]);

                } else {

                    break;

                }

            }

            if (i < args.length) {

                args[0] = args[0]+' '+args.slice(i).join(' ');

            }

            content = args[0];

        } else if (args.length == 1) {

            content = args[0];

        } else {

            content = '';

        }

        return content;

    }

    var originalFunc_log = console.log;

    console.log = function() {

        var content = getContentFromArg(arguments);

        var param = {};

        param.method = 'log';

        param.content = content;

        originalFunc_log.apply(console, arguments);

    }

    var originalFunc_info = console.info;

    console.info = function() {

        var content = getContentFromArg(arguments);

        var param = {};

        param.method = 'info';

        param.content = content;

        originalFunc_info.apply(console, arguments);

    }

    var originalFunc_debug = console.debug;

    console.debug = function() {

        var content = getContentFromArg(arguments);

        var param = {};

        param.method = 'debug';

        param.content = content;

        originalFunc_debug.apply(console, arguments);

    }

    var originalFunc_warn = console.warn;

    console.warn = function() {

        var content = getContentFromArg(arguments);

        var param = {};

        param.method = 'warn';

        param.content = content;

        originalFunc_warn.apply(console, arguments);

    }

    var originalFunc_error = console.error;

    console.error = function() {

        var content = getContentFromArg(arguments);

        var param = {};

        param.method = 'error';

        param.content = content;

        originalFunc_error.apply(console, arguments);

    }

    var uzAttempCheckTimes = 0;

    function uzCheckApiready() {

        if (uzAttempCheckTimes < 50) {

            if (typeof(apiready) === 'function') {

                apiready();

            } else {

                uzAttempCheckTimes++;

                setTimeout('uzCheckApiready()', 100);

            }

        }

    }

    uzCheckApiready();

    (function(){

    var MAX_MOVE = 5;

    var SELECTOR = "tapmode";

    function uz_addTouchedClass(node, clas) {

        if (node && clas) {

            var list = clas.split(' ');

            for (var i=0;i<list.length;i++) {

                var classItem = list[i];

                if (uz_isString(classItem) && classItem.length>0) {

                    node.classList.add(classItem.trim());

                }

            }

        }

    };

    function uz_removeTouchedClass(node) {

        if (node && node.clicker) {

        var clas = node.clicker.clas;

        if (clas) {

                var list = clas.split(' ');

                for (var i=0;i<list.length;i++) {

                    var classItem = list[i];

                    if (uz_isString(classItem) && classItem.length>0) {

                        node.classList.remove(classItem.trim());

                    }

                }

        }

        };

    };

    var Clicker = function(){};

    function parseTapmode(){

    var nodes = document.querySelectorAll('[' + SELECTOR + ']');

      if (nodes) {

    for (var i = 0; i < nodes.length; i++) {

    var node = nodes[i];

                if (!node.uzonclick) {

                    if (node.onclick) {

                        node.uzonclick = node.onclick;

                        node.onclick = null;

                        node.addEventListener('touchstart', uz_handStart, false);

                        node.addEventListener('touchmove', uz_handMove, false);

                        node.addEventListener('touchend', uz_handEnd, false);

                        node.addEventListener('touchcancel', uz_handCancel, false);

                    }

                }

    }

      }

    };

    function uz_isDisabled(e) {

        var node = e.currentTarget;

        return node.disabled;

    };

    function uz_isString(str) {

        return (typeof str == 'string');

    };

    function uz_handStart(e) {

        if (nativeAndroidOrIos.isScrolling) {

            return;

        }

        if (uz_isDisabled(e)) {

            return;

        }

        var node = e.currentTarget;

        var clicker = new Clicker();

        clicker.X = e.touches[0].clientX;

        clicker.Y = e.touches[0].clientY;

        clicker.downTime = e.timeStamp;

        var clas = node.getAttribute(SELECTOR);

        if (!uz_isString(clas)) {

            clas = '';

        }

        clas = clas.trim();

        clicker.clas = clas;

        node.clicker = clicker;

        uz_addTouchedClass(node, clas);

    };

    function uz_handMove(e) {

        if (uz_isDisabled(e)) {

            return;

        }

        var node = e.currentTarget;

        var clicker = node.clicker;

        if (!clicker) {

            return;

        }

        var x = e.touches[0].clientX, y = e.touches[0].clientY;

        if (Math.abs(x - clicker.X) > MAX_MOVE || Math.abs(y - clicker.Y) > MAX_MOVE) {

            uz_reset(node, true);

        }

    };

    function uz_handEnd(e) {

        if (uz_isDisabled(e)) {

            return;

        }

        var node = e.currentTarget;

        uz_reset(node);

        if (!nativeAndroidOrIos.didShowExitAction) {

            uz_fire(e, node);

        }

    };

    function uz_handCancel(e) {

        var node = e.currentTarget;

        uz_reset(node, true);

    };

    function uz_fire(e, node) {

        if (node.uzonclick) {

            var clicker = node.clicker;

            if (clicker) {

                e.preventDefault();

                node.uzonclick.call(node, e);

                node.clicker = null;

            }

        }

    };

    function uz_reset(node, del) {

        uz_removeTouchedClass(node);

        if (del) {

        node.clicker = null;

        }

    };

    parseTapmode();

    })();

    //

    //

    //

    STWKUserContentController.m

    @interface STWKUserContentController()<WKScriptMessageHandler>

    @property (nonatomic,strong) NSMutableDictionary    *delegateInstanceMap; //保存代理实例的字典

    @end

    @implementation STWKUserContentController

    - (instancetype)init {

        if (self = [super init]) {

            _delegateInstanceMap = [NSMutableDictionary dictionary];

            [self injectJS];

        }

        return self;

    }

    /**

    获取一个类的方法列表,并生成js中方法定义

    @param classes 原生类名

    @param level js中类层次定义,

    @return 返回一个类在js中的定义

    */

    - (NSString *)methodByClass:(Class)classes level:(NSString *)level {

        NSMutableString *result = [NSMutableString string];

        unsigned int count;

        __unsafe_unretained Protocol **protocolList = class_copyProtocolList(classes, &count);

        for (int i = 0;i < count;i++) {

            Protocol *protocol = protocolList[i];

            unsigned int methodCount = 0;

            struct objc_method_description *method_description_list = protocol_copyMethodDescriptionList(protocol, YES, YES, &methodCount);

            for (int j = 0; j < methodCount ; j ++)

            {

                struct objc_method_description description = method_description_list[j];

                NSString *name = NSStringFromSelector(description.name);

                NSRange range = [name rangeOfString:@":"];

                if (NSNotFound != range.location) {

                    name = [name substringToIndex:range.location ];

                }

                NSString *str = [NSString stringWithFormat:

                                @"%@.%@ = function() {\n \

                                return uz$e('%@', '%@', arguments, false, 'api');\n \

                                }\n\n",level,name,classes,name];

                [result appendString:str];

            }

            free(method_description_list);

        }

        free(protocolList);

        /*

        Method *methods = class_copyMethodList(classes, &count);

        for (int i = 0; i < count; i++) {

            Method method = methods[i];

            SEL selector = method_getName(method);

            NSString *name = NSStringFromSelector(selector);

            NSRange range = [name rangeOfString:@":"];

            if (NSNotFound != range.location) {

                name = [name substringToIndex:range.location ];

            }

            NSString *str = [NSString stringWithFormat:

                            @"%@.%@ = function() { \

                            return uz$e('%@', '%@', arguments, false, 'api'); \

                            }",level,name,classes,name];

            [result appendString:str];

        }

        free(methods);

        */

        return result;

    }

    - (id)nativeInstanceWithLevel:(NSString *)level {

        return nil;

    }

    - (NSArray *)arrayOfInjectClass {

        return @[@{@"level":@"window.nativeAndroidOrIos",

                  @"classes":@"nativeAndroidOrIosContext"},

                ];

    }

    //根据类名返回实例对象

    - (instancetype)instanceWithClassName:(NSString *)strClass {

        id instance = [_delegateInstanceMap objectForKey:strClass];

        if ( !instance ) {

            Class classes = NSClassFromString(strClass);

            instance = [[classes alloc] init];

        }

        return instance;

    }

    #pragma mark - js注入 -

    - (void)injectJS {

        [self addScriptMessageHandler:self name:@"nativeAndroidOrIos"];

        NSString *ajs = [[NSBundle bundleForClass:[self class]] pathForResource:@"a" ofType:@"js"];

        NSString *jsStr = [NSString stringWithContentsOfFile:ajs encoding:NSUTF8StringEncoding error:nil];

        NSMutableString *jsContent = [NSMutableString stringWithString:jsStr];

        NSArray *arrays = [self arrayOfInjectClass];

        //组合类声明,及函数声明字符串

        NSMutableString *strLevel = [NSMutableString string];

        NSMutableString *strFuncDef = [NSMutableString string];

        for (int i = 0; i < [arrays count];i ++) {

            NSString *level = arrays[i][@"level"];

            NSString *strCls = arrays[i][@"classes"];

            Class classes = NSClassFromString(strCls);

            id instance = [[classes alloc] init];

            [_delegateInstanceMap setObject:instance forKey:strCls];

            [strLevel appendFormat:@"%@={};\n",level];

            [strFuncDef appendString:[self methodByClass:classes

                                                  level:level]];

        }

        //替换a.js中,对象声明及函数定义

        NSString *stringJavaScript = [jsContent stringByReplacingOccurrencesOfString:@"$object_define_placeholder$;" withString:strLevel];

        stringJavaScript = [stringJavaScript stringByReplacingOccurrencesOfString:@"$method_define_placeholder$;" withString:strFuncDef];

        //

        WKUserScript *script = [[WKUserScript alloc] initWithSource:stringJavaScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];

        [self addUserScript:script];

        //

        WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: [self readCurrentCookie] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];

        [self addUserScript:cookieScript];

    }

    //读取cookie信息

    - (NSString *)readCurrentCookie {

        return @"";

        NSMutableString *cookieValue = [NSMutableString stringWithFormat:@""];

        NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];

        for (NSHTTPCookie *cookie in [cookieJar cookies]) {

            [cookieValue appendString:[NSString stringWithFormat:@"document.cookie='%@';",cookie.getCookieString]];

        }

        return cookieValue;

    }

    //

    - (void)onResultCallBack:(AndroidIosNativeBase *)target

                        sign:(NSMethodSignature *)sign

                        inv:(NSInvocation *)inv {

        //获取返回值类型

        const char * returnValueType = sign.methodReturnType;

        //声明一个返回值变量

        __autoreleasing id returnValue; //此处一定要为__autoreleasing否则会crash

        BOOL bVoidReture = NO;

        NSString *strReturnValue;

        //如果没有返回值,也就是消息声明为void,那么returnValue = nil

        if (!strcmp(returnValueType, @encode(void))) {

            NSLog(@"没有返回值,即返回值类型为void");

            returnValue = nil;

            bVoidReture = YES;

        }else if (!strcmp(returnValueType, @encode(id))){

            //如果返回值为对象,那么为变量赋值

            NSLog(@"返回值类型为对象");

            [inv getReturnValue:&returnValue];

            if (!returnValue) {

                strReturnValue = @"null";

            }

            else {

                if ([returnValue isKindOfClass:[NSString class]]) {

                    strReturnValue = returnValue;

                }else {

                    @throw [NSException exceptionWithName:@"返回值异常" reason:@"未转换的返回值类型" userInfo:returnValue];

                }

            }

        }else {

            //如果返回值为普通类型,如NSInteger, NSUInteger ,BOOL等

            NSLog(@"返回类型为普通类型");

            //首先获取返回值长度

            NSUInteger returnValueLenth = sign.methodReturnLength;

            //根据长度申请内存

            void * retValue = (void *)malloc(returnValueLenth);

            //为retValue赋值

            [inv getReturnValue:retValue];

            if (!strcmp(returnValueType, @encode(BOOL))) {

                returnValue = [NSNumber numberWithBool:*((BOOL *)retValue)];

                BOOL bRet = returnValue;

                strReturnValue = [NSString stringWithFormat:@"%@",(bRet ? @"true":@"false")];

            }else if (!strcmp(returnValueType, @encode(NSInteger))){

                returnValue = [NSNumber numberWithInteger:*((NSInteger *) retValue)];

                strReturnValue = [NSString stringWithFormat:@"%ld",returnValue];

            }

        }

        //函数有返回值

        if (!bVoidReture && [target isKindOfClass:[AndroidIosNativeBase class]]) {

            AndroidIosNativeBase *native = (AndroidIosNativeBase *)target;

            [native onResultCallBack:@"[Function]" value:strReturnValue];

        }

    }

    //根据类名,方法名,参数个数查找合适的方法并执行

    - (void)nativeOcMethod:(WKWebView *)webView classes:(NSString *)classes method:(NSString *)strMethod args:(id)param {

        unsigned int count;

        Class classz = NSClassFromString(classes);

        Method *methods = class_copyMethodList(classz, &count);

        for (int i = 0; i < count; i++) {

            Method method = methods[i];

            SEL selector = method_getName(method);

            NSString *name = NSStringFromSelector(selector);

            NSRange range = [name rangeOfString:@":"];

            if (NSNotFound != range.location) {

                name = [name substringToIndex:range.location ];

            }

            if ([name isEqualToString:strMethod]) { //找到对应方法名

                unsigned int argN = method_getNumberOfArguments(method);

                if ([param isKindOfClass:[NSDictionary class]]) {

                    NSArray *args = [param objectForKey:@"param"];

                    if (argN - 2 ==  [args count] ) {    //简单判断参数个数相等,则认为是同一方法

                        id target =  [self instanceWithClassName:classes];

                        if ([target isKindOfClass:[AndroidIosNativeBase class]]) {

                            ((AndroidIosNativeBase *)target).cbId = [[param objectForKey:@"cbId"] integerValue];

                            ((AndroidIosNativeBase *)target).webView = webView;

                        }

                        NSMethodSignature *singture = [classz instanceMethodSignatureForSelector:selector];

                        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:singture];

                        [invocation setTarget:target];

                        [invocation setSelector:selector];

                        //index从2开始

                        for (int i = 0;i <[args count];i++) {

                            id value = args[i];

                            [invocation setArgument:&value atIndex:i + 2];

                        }

                        [invocation retainArguments];  //retain参数 防止被dealloc

                        [invocation invoke];

                        //取消返回值

                        //[self onResultCallBack:target sign:singture inv:invocation];

                        return;

                    }

                }

            }

            NSLog(@"method_getName:%@",name);

        }

    }

    //

    #pragma mark - WKScriptMessageHandler -

    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {

        if ([message.name isEqualToString:@"nativeAndroidOrIos"]) {

            if ([message.body isKindOfClass:[NSDictionary class]]) {

                NSDictionary *body = message.body;

                NSString *method = [body objectForKey:@"method"];

                NSString *class = [body objectForKey:@"class"];

                NSDictionary *params = [body objectForKey:@"param"];//参数

                if ([params isKindOfClass:[NSDictionary class]]) {

                    [self nativeOcMethod:message.webView classes:class method:method args:params];

                }

            }

        }

    }

    @end

    相关文章

      网友评论

          本文标题:webview转wkwebview遇到的问题

          本文链接:https://www.haomeiwen.com/subject/oezfrqtx.html