美文网首页
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