美文网首页
oc & swift混编下的路由实践

oc & swift混编下的路由实践

作者: 小郭哈哈 | 来源:发表于2019-05-27 14:01 被阅读0次

    前言:

    现在有很多第三方路由库,例如:

    1.JLRoute
    2.MGJRouter
    3.HHRouter
    4.FFRouter

    等等这些优秀的第三方库差不多都能满足路由的基本功能

    应用:

    以JLRoute为例:

    先注册一个路由

    JLRoutes.global().addRoute("/ViewController1") { (param:[String : Any]) -> Bool in
        let viewController1 = ViewController1.init()
        pushClassStance.pushViewController(viewController1, animated: true)
        return true
    }
    

    然后跳转

    let url:URL = URL.init(string: "XIAOGUO://ViewController1")!
    JLRoutes.routeURL(url)
    

    一个简单的路由跳转就实现了,当然如果是单纯的跳转到指定页面还是比较好处理的。

    那么这里就会延伸出两个问题
    1、如果页面需要传参该怎么做?
    2、ViewController需要提前注册,如果没有注册的页面就没办法做跳转,能否做动态注册?

    先看第一个问题,JLRoute其实已经提供了带参数的传递方式:

    let url:URL = URL.init(string: "XIAOGUO://ViewController1/userName/xiaoguo/password/123456/isLogin/1")!
    JLRoutes.routeURL(url)
    

    那么我们如何将路由的参数与页面的参数做对应呢?

    JLRoutes.global().addRoute("/ViewController1/userName/:userName/password/:password/isLogin/:isLogin") { (param:[String : Any]) -> Bool in
        let viewController1 = ViewController1.init()
        viewController1.userName = param["userName"] as! String
        viewController1.password = Int(param["password"] as! String) ?? 0
        viewController1.isLogin = (param["isLogin"] as! NSString).boolValue
        pushClassStance.pushViewController(viewController1, animated: true)
        return true
    }
    

    看起来似乎可以处理参数问题,仔细考虑一下如果参数没有提前注册好或者漏掉一些参数那还是无法实现正常传参,能否实现动态传参呢?

    如果是swift类注册路由,传参我们可以利用反射来做类的属性的映射:

     JLRoutes.global().addRoute("/:ViewController") { (param:[String : Any]) -> Bool in
        let vcName = param["ViewController"] as? String ?? ""
        //根据vcName获取对应的控制器
        if let clz = NSClassFromString("\(kCFBundle).\(vcName)") as? UIViewController.Type {
            let vc:UIViewController = clz.init()
            //反射控制器属性
            let vcM = Mirror(reflecting: vc)
            //過濾參數關鍵字
            let filterParam = param.filter({ (arg0) -> Bool in
                let (key, _) = arg0
                return key != "JLRoutePattern" && key != "JLRouteURL" && key != "JLRouteScheme" && key != "ViewController"
            })
            //找到对应的控制器属性并赋值
            for (key,value) in filterParam {
                for child in vcM.children {
                    if key == child.label {
                        //print("key:\(key),child.label:\(String(describing: child.label))")
                        vc.setValue(value, forKey: key)
                    }
                }
            }
            pushClassStance.pushViewController(vc, animated: true)
            return true
        }
        return false
    }
    

    发起跳转统一用标准url传参方式(?&):

    let url:URL = URL.init(string: "XIAOGUO://VC3?userName=xiaoguo&password=123456&isLogin=1")!
    JLRoutes.routeURL(url)
    

    这样就完成动态传参的问题。

    这里会发现通过反射只能在swift页面内部之间跳转,如果跳转的页面是oc页面则反射不到对应的属性,那要如何处理呢?

    只能通过Runtime的class_copyPropertyList 或class_copyIvarList获取类的属性(swift属性必须声明为@objc赋予动态性)

    改成class_copyPropertyList方式:

    JLRoutes.global().addRoute("/:ViewController") { (param:[String : Any]) -> Bool in
                let vcName = param["ViewController"] as? String ?? ""
                //oc类
                let ocClz = NSClassFromString("\(vcName)") as? UIViewController.Type
                //swift类
                let swfClz = NSClassFromString("\(kCFBundle).\(vcName)") as? UIViewController.Type
                //根据vcName获取对应的类(oc/swift)
                if let clz = ocClz ?? swfClz {
                    //過濾參數關鍵字
                    let filterParam = param.filter({ (arg0) -> Bool in
                        let (key, _) = arg0
                        return key != "JLRoutePattern" && key != "JLRouteURL" && key != "JLRouteScheme" && key != "ViewController"
                    })
                    let vc:UIViewController = clz.init()
                    //Runtime获取类属性
                    var count:UInt32 = 0
                    let propertyList = class_copyPropertyList(clz, &count)
                    //找到对应的类属性并赋值
                    for (key,value) in filterParam {
                        print("key:\(key),value:\(value)")
                        for i in 0..<numericCast(count) {
                            let property = property_getName((propertyList?[i])!);
                            let proper = String.init(cString: property)
                            if key == proper {
                                vc.setValue(value, forKey: key)
                            }
                        }
                    }
                    //释放内存
                    free(propertyList)
                    pushClassStance.pushViewController(vc, animated: true)
                    return true
                }
                return false
            }
    

    这样就可以兼容oc页面的传参了


    完整Demo:
    https://github.com/sg369326973/routerTest/

    相关文章

      网友评论

          本文标题:oc & swift混编下的路由实践

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