YZZRouter 1.1.0

YZZRouter 1.1.0

YZZRouter 维护。



YZZRouter 1.1.0

  • 杨欢

YZZRouter

为什么需要一个新路由器

JLRoutesGithub 中的 starred 数量最多。它支持一个 scheme 的多处理,但处理函数应该返回 Bool 来指示处理是否成功,所以在处理中不支持异步工作。并且 JLRoutes 最明显的缺陷是其匹配算法不够高效,它应该遍历在每次匹配时注册的所有路由。

其他 Routers 是为 VC 转换设计的,这在大多数情况下并不通用。

YZZRouter 的设计类似于 Web 侧的 Router。如果您曾经使用过 ExpressKoa 等库,您会对 YZZRouter 感到熟悉。

YZZRouter 支持

  • 高效(使用 trie 进行注册和匹配)
  • 异步工作
  • 中间件
  • 解析变量,处理通配符

如何安装

cocoapods

target 'MyApp' do
      pod 'YZZRouter'
end

如何使用

注册

在注册之前,应该实例化RootRouter类。由于最好通过不同的RootRouter实例来隔离路由,因此YZZRouter不支持用于注册的类方法。例如,您有VC之间的转接路由,同时还有服务路由,应使用不同的实例而不仅仅是不同的方案。

RootRouter *route = [RootRouter router];

注册块处理器

  [router add:@"aaht://example.com/newRouter" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
        completion(RComplete);
    }];

注册多个块处理器

    RouterBlockHandle *handle1 = [[RouterBlockHandle alloc] initWithHandleBlock:^(RouterRequest *req, routerHandleCompletion completion) {
       NSLog(@"hello world1");
       completion(RNext);
    }];

    RouterBlockHandle *handle2 = [[RouterBlockHandle alloc] initWithHandleBlock:^(RouterRequest *req, routerHandleCompletion completion) {
    NSLog(@"hello world2");
    completion(RComplete);
}];

  [router add:@"aaht://example.com/newRouter" handles:@[handle1, handle2]];

为多个URL注册处理器

  [router add:@[@"aaht://test1", @"aaht://test2"] blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
        completion(RComplete);
    }];

按方案或公共前缀组织您的路由

  //organize by scheme
  Router *r = [router scheme:@"empty"];
  [r add:@"/th/e/te/st/path" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
      completion(RComplete);
  }];

  [r add:@"/th/e/te/:id" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
      completion(RComplete);
  }];

  //organize by path
  Router *pathR = [router path:@"empty://t/net"];

  [pathR add:@"/f" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
      completion(RComplete);

  }];

  [pathR add:@"/1/2/3" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
      completion(RComplete);
  }];

  [router startUrl:@"empty://t/net/f" completion:nil];

支持变量

  [router add:@"aaht://example.com/:id/:name/newRouter"  blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
      [req.userInfo addEntriesFromDictionary:req.variables];
      completion(RComplete);
  }];

 [router startUrl:@"aaht://example.com/123/hello/newRouter" autoRedirect:YES completion:^(RouterRequest *c) {
    XCTAssertEqualObjects(c.variables, @{
                              @"id" : @"123",
                              @"name" : @"hello",
                              });
  }];

使用三重通配符:**

** 作为多重通配符。多重通配符非常适用于具有相同模式的任务,如认证检查、日志、跟踪等。

例如:hhh://hel/**,使用此URL注册的处理器将匹配任何以hhh://hel/为前缀的URL。

一个非常通用的用途是在路由之前进行认证检查。可以使用路由器这样实现。

  [router add:@"aaht:///**"  blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
    if (!login) {
      completion(RComplete);
    } else {
      completion(RNext);
    }
  }];

所有具有方案:"aaht"的URL都将检查是否已登录。如果没有登录,则不会执行左边的处理器。

///表示此URL的主机为空。不要使用aaht://**,因为在iOS标准库中解析它是非法的。

如果整个URL都是**,则表示它是一个全局处理器,任何URL都将匹配,您可以使用这个来处理与每个路由相关的任务,如日志、跟踪。

将常用逻辑作为处理器提取

处理器在Web端和中间件有相同的概念。将您的常用逻辑提取为处理器是最佳实践,例如认证处理器、跟踪处理器、A/B测试处理器。

您可以使用YZZRouter提供的blockHandle或通过实现协议RouterHandleProtocol来自定义您自己的处理器,该协议只有一个函数。

- (void)handleWithRequest:(RouterRequest *)req completion:(routerHandleCompletion)completion;

请求包含您在URL中需要的所有信息,如queryItems、变量和原始信息(如URL、方案、主机等)。此外,您还可以使用userInfo,这是一个可以存储您想要的任何内容的可变字典。

非常重要的一点是:记住在处理器中只调用一次completion()

completion的参数是一个枚举。

typedef NS_ENUM(NSUInteger, RouterHandleResult) {
    RNext,
    RComplete,
    RRedirect,
    RFailed,
}

RNext表示“下一个处理器将被执行”。

RComplete表示终止处理器执行,剩下的处理器将不会执行。

RRedirect如果您在处理器中更改了URL并希望开始一个新的匹配,则可以使用重定向。一个简单的示例,A/B测试处理器。

    NSString *abtestUrl = @"abtest://nothing/hello";
    NSString *helloabtestA = @"testScheme://nothing/hello/A";
    NSString *helloabtestB = @"testScheme://nothing/hello/B";
    NSString *abtestMock = helloabtestA;

    [router add:abtestUrl blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
    [req reuseWithUrl:abtestMock];
      completion(RRedirect);
    }];
    [router add:helloabtestA blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
      completion(RComplete);
    }];

    [router add:helloabtestB blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
      completion(RComplete);
    }];

    // will match helloabtestA
    [router startUrl:abtestUrl autoRedirect:YES completion:nil];

RFailed将停止执行处理器,类似于RComplete

转移处理器

除了默认的blockHandle之外,YZZRouter还支持用于VC转换的转换处理器。您可以使用它如下

   [router add:@"sf://A/main" handle:[[RouterTransferHandle alloc] initWithVCClass:MainViewController.class];

当您启动URL @"sf://A/main"时,它将自动转换为MainViewController。将转换处理器作为URL注册中的最后一个处理器更好。

如果您想要更自定义的转换,只需遵守RouterHandleProtocol并实现您自己的处理器。

高级用法

处理支持异步工作

handls support async work like network. Just Make sure completion called in proper time and must be called.

处理中的AOP

有一个简单的例子,每一项都会记录执行时长。

    [router add:@"**" blockHandle:^(RouterRequest *req, routerHandleCompletion completion) {
        NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
        completion(RNext);
        NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
        NSLog(@"duration: %f", end - start);
    }];

注意 completion(RNext),这个代码之前和之后的代码分别表示 在处理左边执行之前在处理左边执行之后。这里的 completion() 并不代表返回,如果当前处理依赖于左边的执行结果,请将代码放在 completion(RNext) 之后。

白名单处理

在 RootRouter 中设置方案的白名单。

- (void)configSchemeWhiteList:(NSSet *)list;

或者使用 RouterWhiteListHandle 自定义白名单方式。

用全局 TrillingWildCard: ** 注册一个 RouterWhiteListHandle 用于方案白名单。

调试

使用 Router 的功能。

- (NSDictionary *)debugRouterTrieTree;

Trietree 的形式查看所有注册信息。

使用 Router 的功能。

- (NSDictionary *)allRouteRegisterInfo;

您可以通过以下方式获取注册路径和处理程序:

"ROOT/**" = "RouterWhiteListHandle,RouterBlockHandle"; "ROOT/KDefaultSsheme!/t/net/1/2/3" = RouterBlockHandle;

RouterHandleProtocol 有一个可选的属性:name。如果您实现这个属性,可以像之前一样获取这些信息,否则它会返回处理程序类的字符串。

其它内容

问题和MR都欢迎!