webview转wkwebview遇到的问题

2019-01-03  本文已影响0人  zttjhm

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

上一篇下一篇

猜你喜欢

热点阅读