美文网首页
iOS(Swift) 路由设计二

iOS(Swift) 路由设计二

作者: 简单coder | 来源:发表于2023-02-25 17:05 被阅读0次

    2023-02-26 今天难得的一个好天气,一整天阳光暖洋洋,精力十足,整理框架时,对路由一些小想法,于是花了一整天,把整个路由组件做成了pod,抽了出来.

    上一篇路由讲了个当时随手写的一个框架,但写完后项目没怎么用,后面因为事情太多,渐渐也就忘了,这次重新整理了一下思路,对路由重新设计了一番.

    架构

    |____Classes
    | |____GetRouter.swift
    | |____GetRouterHandlerSource.swift
    | |____.gitkeep
    | |____ReplaceMe.swift
    | |____GetRouterMiddleware.swift
    | |____GetRouterName.swift
    | |____GetRouterHandler.swift
    |____Assets
    | |____.gitkeep
    

    路由设计

    路由分远程路由和本地路由,我们需要做的是统一,远程路由适用与banner跳转,js交互操作等等情况,一般来说是string执行的路由跳转,本地路由需要规定要,一个页面的跳转,最好走唯一的路由,好处就是封装,低耦合,A页面理论上是不应该import B页面的,还有就是埋点参数上报等等,统一路径.

    流程

    • 远程路由
      urlStirng->encoding->parse解析路由,合并参数->匹配本地路由->执行跳转
    • 本地路由
      匹配本地路由->执行跳转

    GetRouter

    远程路由与本地路由解析的流程文件后,解析完成后交给GetRougerHandler执行

    GetRougerHandler

    匹配路由名相同的GetPage 执行中间件拦截, 执行跳转

    GetRouterMiddleware中间件拦截

    中间件用于公共拦截方式,举例说明,我们如果要想跳转vip直播间,我们需要前置判断当前登录者是不是vip,当前vip直播间是否正在直播,一个是同步就可以判断出当前用户vip状态,一个需要实时请求后端直播状态接口等.
    传统方式中,我们可能需要通用写个判断

    // 判断vip
    guard User.isVip else {
    return 
    }
    // 判断直播状态
    RequestLiveStatus {
      jumpLive()
    }
    

    这样写不太优雅, 所以我增加了同步拦截与异步拦截(注意,中间件的环境在iOS13以上处于异步线程,执行UI操作,吐丝重定向等请回到主线程执行)

    open class GetRouterMiddleware {
        /// 优先级
        let priority: Int
        
        public init(priority: Int) {
            self.priority = priority
        }
        /// 处理返回结果
        /// - Returns: true: 允许通过 false: 禁止
        open func handler(routeName: GetRouterName, params: GetDict?) -> Bool { true }
        
        @available(iOS 13.0.0, *)
        /// 异步中间件, 注意如果想要在此出重定向路由,注意要回到mainThread
        /// - Parameter routeName: 路由名
        /// - Returns: true: 允许通过 false: 禁止
        open func handlerAsync(routeName: GetRouterName, params: GetDict?) async throws -> Bool { true }
    }
    
    这里注意一下,异步拦截是iOS13以上可用,需要自行构造一个async任务(相关只是可以参考await async 构造async),demo也有相关示例,使用起来也很方便

    GetPage

    注册路由时包装的路由匹配信息,包括中间件和行为.

    GetRouterHandlerSource

    提供GetPage的source来源

    GetRouterName路由名设计

    原先我们设计的路由名是这样的

    enum GetRouterName: String {
        // MARK: - --------------------------------------公共
        case http = "http"
        case https = "https"
        // MARK: - --------------------------------------发现
        
        // MARK: - --------------------------------------社区
        case community_post_detail = "community_post_detail"
        // MARK: - --------------------------------------个人
        
        // MARK: - --------------------------------------我的
        case mine_setting = "mine_setting"
        case mine_complete = "mine_complete"
        // MARK: - --------------------------------------帖子
        case post_detail = "post_detail"
    }
    

    很简单,很易懂,但是有缺点

    • enum可以在switch中可以枚举掉所有的选项,保证我们不会漏掉任何路由名,但是,enum无法写成extension,也就是说,我们必须把所有名卸载一个文件里,这样对模块化是不好的,我希望使用的场景是我每个模块去注册这个路由,然后提供我模块的模块名和路由处理选项,这样就可以做到单独开发自己的选项
    • string不是类型,在本地调用中,我们不可避免的会出现必须强制带类名的方式,写起来就不是那么清爽,例如Router.to(GetRouterName.mine_setting),我想要的是Router.to(.mine_setting)
      所以,利用swift的语法特点,我修改成这种写法
    /// 路由列表容器
    public struct GetRouterNames {}
    
    /// 路由列表
    public struct GetRouterName: RawRepresentable,
                          ExpressibleByStringLiteral,
                          Hashable {
        
        public var rawValue: String
        
        public init(rawValue: String) {
            self.rawValue = rawValue
        }
        
        public init(stringLiteral value: StringLiteralType) {
            self.rawValue = value
        }
        
        public var hashValue: Int {
            rawValue.hashValue
        }
    }
    

    这里写了一个类型GetRouterName,利用swift字面量,可以达到这种定义路由名的效果

    //  GetRouter+Home.swift
    //  quick
    //
    //  Created by suyikun on 2023/2/25.
    //
    
    import Foundation
    
    extension GetRouterNames {
        /// 发现列表
        static let discover: GetRouterName = "/discover/list"
        /// 发现页详情
        static let discoverDetail: GetRouterName = "/discover/detail"
    }
    

    虽然,我们损失了enum强制枚举所有枚举的效果,但是,我们收获了模块化路由名+路由名类型的优点

    使用示例




    注意事项: 获取参数时,远程路由获取的value是string类型,需要在写代码时考虑兼容转换

    自己在业务跳转方面,做好兼容即可, 例如下图这样处理

    PS

    在我看来,路由设计应该是可以纯粹的解耦合的,他跟业务的跳转没有一点关系,只是一些规则的解析,
    总结一下,回顾了下整个路由设计,一天的收获满满,如果你们喜欢我的文章,或者对路由设计有什么批评建议意见,希望可以点赞留言
    最后留一下git地址: 戳这前往

    相关文章

      网友评论

          本文标题:iOS(Swift) 路由设计二

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