美文网首页
Swift简版路由

Swift简版路由

作者: liang1991 | 来源:发表于2019-12-22 14:33 被阅读0次

    我眼中的路由

    提到路由我最先联想到的是平时用来上网的无线路由器,而无线路由给我们带来的好处在我看来有两点。一、用户不用关心无线路由是连接的网线、还是一个桥接的路由器,只需要使用账号密码即可上网。二、我们更换上网方式比如拨号上网换个账号,只要保持路由器wifi名称账号密码不变,用户就可以不做任何更改继续使用wifi。

    在我看来路由就是一个映射规则,通过输入得到输出。就像无线路由器一样输入的是wifi的名称密码,得到的是网络数据。我们定义好生成规则后就可以很简单的从输入得到输出结果。

    那移动开发中的路由是什么呢?以iOS开发为例在我看来,就是一个根据规则生成控制器、视图等的一个东西。

    1、开发中使用路由的好处

    个人理解和无线路由器好处类似。一、按照某种规则比如用链接和页面建立对应关系后,我们通过链接就可以构造出对应的页面、控件,调用者和被调用者没有直接依赖也不用关心具体的初始化步骤。二、当我们修改映射结果时,比如以前链接url对应的是页面A现在换成页面B,调用的地方不用做任何修改,更加灵活。提到路由往往就会讲到组件化,路由是组件化中很重要的一部分但不在本篇文章的讨论范围,这里推荐一篇文章大家有兴趣可以看看iOS 组件化方案探索

    2、我们项目的需求

    ①、通过h5、远程推送、公众号等打开app跳转到指定页面。②、应用内有个任务系统,可能跳转到很多不同页面。③、一些目标页需要登录后方可进入、一些目标页需要先出一个询问弹窗点击确定后才会跳转。

    3、页面调用方式

    ①、app外调用分为三种远程推送、UniversalLink、URL Schemes。②、应用内调用
    ③、其中推送和应用内调用传参客户端可以随意设定,主要是看deeplink和url schemes这两种,而这两种都是以链接的形式打开app的,所以我们就以链接来和页面建立绑定关系。

    4、使用链接和页面建立绑定关系

    ①、简单介绍下链接的组成部分
    例如:https://www.baidu.com/s?inputT=3358&rsv_sug4=3358
    scheme(https)、host(www.baidu.com)、path(/s)、参数(?inputT=3358&rsv_sug4=3358)
    ②、首先建立绑定关系
    由于使用deeplink打开使用的是https、使用urlscheme使用的是自己定义的一个字符串,所以scheme是不固定的。每个链接参数肯定也是不固定的,所以我们选用host+path来作为key对应具体的页面。

    protocol AIRouterProtocol {
        static func targetWith(pa: [String: Any]) -> AIRouterProtocol?
        func needLogin() -> Bool
        func isPush() -> Bool
    }
    
    var targetDict = [String: AIRouterProtocol.Type]()
    
    func registerRouter(target: AIRouterProtocol.Type, key: String) {
        targetDict.updateValue(target, forKey: key)
    }
    

    如上面代码所示我们将页面和链接的映射关系存储在了一个字典里,以连接的host+path为key,以一个遵从我们定义的路由协议为value。协议主要定义了三个方法,targetWith(页面的构造方法)、needLogin(页面是否需要登录)、isPush(页面跳转方式)。

    5、通过链接获取页面

    ①、获取链接各个组成部分,取出host+path作为key值获取对应页面,取出链接中的参数部分初始化页面。考虑到应用内使用时传递一些链接无法传递的参数类型,如block、UIImage等,提供了一个externParameter字典类型参数和链接里的参数共同组成参数部分来初始化页面。

     func targetWith(urlStr: String, externParameter: [String: Any]? = nil) -> AIRouterProtocol? {
            let encodeUrlStr = urlStr.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
            if let urlComponents = URLComponents(string: encodeUrlStr) {
                let scheme = urlComponents.scheme ?? ""
                let host = urlComponents.host ?? ""
                let path = urlComponents.path
                //AILog("scheme:\(scheme) host:\(host) path:\(path)")
                var parameter = [String: Any]()
                if let queryItems = urlComponents.queryItems {
                    for query in queryItems {
                        parameter.updateValue(query.value ?? "", forKey: query.name)
                    }
                }
                if let externDic = externParameter {
                    for (key, value) in externDic {
                        parameter.updateValue(value, forKey: key)
                    }
                }
                if scheme == kAppScheme {
                    return targetWith(key: host + path, parameter: parameter)
                } else if kHttp.contains(scheme) {
                    if let target = targetWith(key: host + path, parameter: parameter) {
                        return target
                    }
                }
            }
            return nil
        }
        
        func targetWith(key: String, parameter: [String: Any]) -> AIRouterProtocol? {
            if let router = targetDict[key] {
                return router.targetWith(pa: parameter)
            }
            return nil
        }
    
    6、调用

    通过链接初始化页面,然后通过协议约定的方法获取是否需要登录、跳转方式完成跳转。(如果不需跳转初始化方法返回nil即可,然后做自己想做的事儿,如展示一个弹窗、tabbar切换选中tab等)

        /// 处理链接(打开页面/其它处理)
        /// - Parameter urlStr: 链接
        /// - Parameter externParameter: 额外参数(一些参数无法放在链接中如block、UIImage等可以放在这里)
        static func openUrl(urlStr: String, externParameter: [String: Any]? = nil) {
            if let target = AIRouter.share.targetWith(urlStr: urlStr, externParameter: externParameter) {
                let needLogin = target.needLogin()
                let isPush = target.isPush()
                if let vc = target as? UIViewController {
                    self.openVC(vc: vc, needLogin: needLogin, isPush: isPush)
                }
            }
        }
        
        static func openVC(vc: UIViewController, needLogin: Bool, isPush: Bool) {
            if let topVC = UIViewController.topViewController() {
                if needLogin && UserManager.share.UserIsLogin == false {//登录处理
                    let loginVC = LoginViewController {
                        self.openVC(vc: vc, needLogin: needLogin, isPush: isPush)
                    }
                    self.openVC(vc: loginVC, needLogin: false, isPush: true)
                } else {
                    if isPush {
                        if let _ = topVC.navigationController {
                            topVC.aiPushToVC(toVC: vc)
                        } else {
                            let navi = UINavigationController(rootViewController: vc)
                            topVC.aiPresent(navi, animated: true, completion: nil)
                        }
                    } else {
                        topVC.aiPresent(vc, animated: true, completion: nil)
                    }
                }
            }
        }
    
    7、详见Demo

    MBlogDemo/AIRouterDemo

    相关文章

      网友评论

          本文标题:Swift简版路由

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