布景&痛点

跟着CSDN App技术栈的扩展,从本来最最简略的原生页面到原生页面的跳转,扩展到现在同一个App中包括原生页面、H5页面、小程序页面、Flutter页面之间的跳转。

iOS 动态路由实现方案
这样带来的问题是:跟着App的版别迭代,许多本来原生完成的页面,需求经过新的H5页面进行升级/降级,或许本来PC或许H5页面,需求重定向到已有的原生页面。而这些基本都是硬编码的跳转逻辑,需求跟着版别不停改动。总结下来,现有的,各个技术栈隔离的页面跳转逻辑面临的直接问题有:

  • 跳转的逻辑需求依据版别迭代走,无法一致远程进行改动(例如:每次新增一个需求阻拦跳转原生的页面,都需求经过发版来处理)
  • 跨技术栈跳转的完成成本比较高,有必要在桥接模块中进行特别适配
  • H5页面中,某些跳转需求跳转原生或许其他页面,有必要要经过WebView跳转的阻拦做特别判别处理

为了处理以上硬编码及灵活性差的问题,咱们经过整理现有的各技术栈跳转逻辑,将这些跳转整合,能够满意动态性、可装备的需求。

路由的含义

首先要明确的是,路由并非只是指的界面跳转,还包括数据获取,事务处理。

前端的路由

网络中路由概念是指路由器从一个接口上接纳到数据包,依据数据包的目的地址进行定向转发到另一个接口的进程。直白一点便是,路由是一种数据的搜集和分发进程。在前端开发中,路由的作用首要是确保视图和 URL 的信息同步,用户能够经过手动输入或许与页面进行交互来改变 URL,然后程序向服务端发送恳求获取资源,接着从头绘制 UI。iOS 移动端能够借用前端的思路,遵从约好的路由协议,经过某种手段映射到详细的视图组件/控制器/功用等资源。

移动端(iOS)的路由

URI

一致资源标识符(Uniform Resource Identifier)是一种用于标识互联网资源的字符串,此种标识答运用于对网络中的资源经过特定的协议进行交互操作。URI 最常见的形式便是一致资源定位符 URL。

iOS 动态路由实现方案
这是一段URL,每一段都代表类对应的含义,咱们能够了解 URL 为一段携带了获取到某资源的一切有必要的信息的特定组合字符串。

URL Scheme

iOS 体系里面支持的 URL Scheme 办法翻开运用。咱们能够经过 TARGETS -> Info -> URL Types 添加运用的 Scheme,该 Scheme 能够了解为 App 的一个身份标识,用它能够翻开咱们的运用(在三方共享时常常需求咱们去渠道生成自己的 Scheme,三方渠道用此字段跳转咱们App)。

iOS 动态路由实现方案
这样,咱们运转App之后,经过 Safari 就能够跳转到咱们的App。输入 csdnapp:// 跳转。

你也能够经过 info.plist 文件中的 URL types 字段管理你的 URL Scheme 信息。即便你没有用过 URL Scheme,那么你一定运用过一些体系的服务,例如拨打电话,运用体系邮箱等功用。他们的协议头就像下面这样:

mailto://
tel://110

假如你未运用过,那么最直观的,你在手机的体系浏览器 Safari 中输入上面的命令,体系就会提示你是否拨打电话或许编写邮件。tel://mailto:// 便是体系电话和邮件运用的路由协议头。

一些热门的运用同样界说了自己的路由协议头,例如QQ、微信、微博等:

mqq://
weixin://
sinaweibo://
接纳处理

经过 -openURL: 过来的的音讯,咱们能够经过 AppDelegate 中的回调署理进行接纳处理:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options

Universal Links

运用 URL Scheme 有两个弊端,第一个便是紊乱,由于 URL Scheme 是自界说字段,任何App都能够运用 weixin:// 这就可能导致体系跳转错误,这种状况在公司开发一系列的运用时常常发生。第二个便是假如用户未装置与URL Scheme对应的运用时,体系则无法正常跳转,这时通常需求咱们程序员手动判别是否能够翻开此 URL : -canOpenURL:,然后引导用户去装置对应的运用。

在 iOS 9.0 之后,苹果新增了一项功用 Universal Links,直译便是通用链接,这个功用让咱们能够经过一般的 HTTP 链接就能发动咱们的 App。

运用 Universal Links 跳转运用的优点便是:

假如装置了App,无论是在体系浏览器 Safari 里,仍是在其他运用了webView控件的页面中,都能够翻开App。

假如没有装置App,就会翻开对应的网页,这个网页能够是宣扬官网,又或许是下载装置地址。

从iOS 体系里面支持的URL Scheme办法,咱们能够看出,关于一个资源的拜访,苹果也是用URI的办法来拜访的。

处理了什么问题

iOS 动态路由实现方案
借用iOS 组件化 —— 路由规划思路分析一文中的图,很形象的展现了项目中各个控制器模块之间错综复杂的联系。 咱们项目中运用的路由组件是依据MGJRouter进行二次开发完成的路由组件CNRouter,各种页面之间的跳转都能够经过 URL 的办法进行路由, 运用Router之后大概是这样:
iOS 动态路由实现方案

过程
  • APP内每个页面控制器都需求对应一个路由地址URL
  • 路由页面跳转前,需求将对应的路由地址URL注册到CNRouter
  • 不同页面跳转时,将音讯发送到CNRouter中一致处理
  • CNRouter依据其注册的URL来找到对应信息,然后担任实例化,解析参数,跳转页面等事务处理

经过上述过程,咱们能够看到,每个控制器之间并不需求相互依赖对方,能够完美的处理不同模块之间的耦合。

动态路由的价值

依据CNRouter URL跳转,咱们又完成了动态路由功用,它的责任首要有:

  • 承担 App 内一切页面跳转逻辑
  • 经过Apollo装备,支持获取/装备路由替换规矩
  • 匹配一切的路由跳转规矩,射中路由规矩的直接进行跳转
  • 将实践跳转方针地址传递给路由组件履行实践的跳转行为(路由重定向)
    iOS 动态路由实现方案

完成方案

1、 路由注册与跳转

整个CNRouter便是由一个NSMutableDictonary *routes 控制的,routes中存放了一切已经注册的URL。

@interface CNRouter ()
/**
 *  保存了一切已注册的 URL
 *  结构类似 @{@"blog": @{@":blogId": {@"_", [block copy]}}}
 */
@property (nonatomic) NSMutableDictionary *routes;
@end

iOS 动态路由实现方案
经过以下办法结构路由匹配规矩的字典。

- (NSMutableDictionary *)addURLPattern:(NSString *)URLPattern
{
    NSArray *pathComponents = [self pathComponentsFromURL:URLPattern];
    NSMutableDictionary* subRoutes = self.routes;
    for (NSString* pathComponent in pathComponents) {
        if (![subRoutes objectForKey:pathComponent]) {
            subRoutes[pathComponent] = [[NSMutableDictionary alloc] init];
        }
        subRoutes = subRoutes[pathComponent];
    }
    return subRoutes;
}

例如注册以下两条路由:

[CNRouter registerURLPattern:@"https://blog.csdn.net/:userName/article/details/:articleId"  toHandler:(NSDictionary *routerParameters) {
}];
[CNRouter registerURLPattern:@"csdnapp://app.csdn.net/blog/detail"  toHandler:(NSDictionary *routerParameters) {
}];

按照上面结构路由匹配规矩的字典的办法,该路由规矩字典就会变成这个样子:

{
    https = {
        blog.csdn.net = {
            :userName = {
                article = {
                    details = {
                        :articleId = {
                            _ = < __NSMallocBlock__: 0x282240ac0 > ;
                        };
                    };
                };
            };
        };
    };
    csdnapp = {
        app.csdn.net = {
            blog = {
                detail = {
                    _ = < __NSMallocBlock__: 0x282241bc0 > ;
                };
            };
        };
    };
}

路由规矩字典生成之后,比及路由匹配的时分就会遍历这个字典。一切注册的路由,都是以这种办法存放在routes中。 当有路由跳转时,调用以下办法:

[CNRouter openURL:@"https://blog.csdn.net/weixin_36162680/article/details/123161859" withUserInfo:@{@"isLogin": @YES} completion:(id result) {
    NSLog(@"result = %@",result);
}];

上面的URL会匹配成功,那么生成的参数字典结构如下:

{
	userName = "weixin_36162680";
	CNRouterParameterUserInfo = {
		isLogin = 1;
	};
	CNRouterParameterURL = "https://blog.csdn.net/weixin_36162680/article/details/123161859";
	articleId = "123161859";
}

由上可见,路由组件CNRouter,它首要担任:

  • 发动时注册路由和页面
  • 查询正确的页面进行跳转或许履行其他事务逻辑
    iOS 动态路由实现方案
    iOS 动态路由实现方案

2、 动态路由与规矩装备

概念

动态路由:大略的讲便是指你的URL地址与页面或许组件之间的映射联系。 本项目会依据Apollo装备,经过远端下发的办法,去动态构建这个路由映射表。完成动态显示可跳转的页面或组件。

路由重定向

关于移动端的路由重定向,实践上便是将一个路由转换为另一个路由,例如: https://live.csdn.net/room/:id 转换为: csdnapp://app.csdn.net/live/room?id=xxxx

规矩装备

路由重定向中的一个关键节点便是“装备”,咱们需求一个路由规矩列表来记录和下发匹配规矩。为了方便下发路由规矩表,咱们将这份装备表存放在Apollo,动态地下发到客户端。

一条路由规矩,分为一个 Key 和对应的 Value,Key 为需求注册的路由(匹配规矩),Value 为需求实例化的控制器称号或许需求重定向的URL,运用 JSON 格式界说。 例如:

{
    "https://blog.csdn.net/:userName/article/details/:articleId": {
        "class": "CNNewBlogDetailViewController"
    },
    "https://live.csdn.net/room/:id": {
        "redirectUrl": "csdnapp://app.csdn.net/live/room"
    }
}

流程

在APP发动阶段拉取正确的路由表(映射规矩),进行注册并且保存下来。

iOS 动态路由实现方案

实践代码完成中,咱们在页面履行跳转前,判别是否有重定向路由,假如有重定向路由,则履行重定向跳转,否则直接初始化方针控制器。

iOS 动态路由实现方案
iOS 动态路由实现方案
代码完成如下:

[CNRouter cn_addRoute:router handler:(NSDictionary *routerParameters) {
    //判别是否有重定向
    if ([routerParameters[kCNRouteClassRedirectUrl] isValid]) {
        //跳转重定向地址
        [CNRouter cn_openURL: routerParameters[kCNRouteClassRedirectUrl] withUserInfo: routerParameters];
    }else{
        //初始化控制器
        [self executeRouterClassName:className parameters: routerParameters];
    }
}];

实践运用

先来说下运用动态路由的布景,CSDN APP中技术树模块是用小程序来开发的,现在需求将PC URL在APP中不用H5翻开,需求用小程序来展现页面。而APP小程序的URL与PC的URL是不一致的,假如直接用PC URL的话,会存在无法跳转小程序问题。以私信中技术树跳转为例:

  • iOS中技术树小程序的路由URL:csdnapp://app.csdn.net/mpTinyApp?id=__UNI__C92EAF9?language=cloud_native
  • PC端私信中发送的URL: https://edu.csdn.net/skill/cloud_native

那么处理这一问题,只需求在 iOS 的动态路由添加一个规矩,将 https://edu.csdn.net/skill/cloud_native 动态替换成 csdnapp://app.csdn.net/mpTinyApp?id=__UNI__C92EAF9&language=cloud_native 就能够了。 装备如下:

{
    "https://edu.csdn.net/skill/:language": {
        "redirectUrl": "csdnapp://app.csdn.net/mpTinyApp?id=__UNI__C92EAF9"
    }
}

iOS 动态路由实现方案