Swift简版路由

2019-12-22  本文已影响0人  liang1991

我眼中的路由

提到路由我最先联想到的是平时用来上网的无线路由器,而无线路由给我们带来的好处在我看来有两点。一、用户不用关心无线路由是连接的网线、还是一个桥接的路由器,只需要使用账号密码即可上网。二、我们更换上网方式比如拨号上网换个账号,只要保持路由器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

上一篇下一篇

猜你喜欢

热点阅读