WAAppRouting 0.0.5

WAAppRouting 0.0.5

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布日期最后发布2015年11月

Marian Paul 维护。



  • 作者:
  • Marian Paul

WAAppRouting

Ipodishima 开发和维护,他是在 Wasappli Inc 的创始人兼首席技术官。(如果您需要开发应用程序,请联系我们的团队!}}

那么这个库有什么用呢?好问题。让我们通过提出另一个问题来回答。你有没有在某些时候遇到过以下问题?

  • 嗯,我需要给我的应用中的一些部分添加一些快捷方式,手动分配路径和选择我需要的控制器看起来很糟糕。
  • 我用 push 视图控制器的方法已经厌倦了。
  • 我希望有一种某种 url 处理方法,可以像甩动手指一样轻松地到达应用中的指定部分。
  • 如果它可以处理控制器堆栈,那就太棒了。
  • 我找到了一个库,但它与我的自定义容器不兼容。
  • 我找到一个很棒的库,但路由匹配不符合我的要求...

所有这些问题都由 WAAppRouting (以及更多)回答

哪些使用?

  • 对于所有 iOS:在您的应用中启用链接。告诉您的应用使用一些魔法转到 home/events/3/register 总是很有用的。
  • 对于 iOS 9:支持深度链接(如 Twitter 应用)。打开此 URL Twitter 上的我 将直接打开应用而不是网站。
  • 对于 iOS 9:响应 搜索事件。通过使用 CoreSpotlight,用户可以通过打开类似预订的搜索结果进入您的应用。在这种情况下,您需要考虑使用 goto://bookings/bookingFromSearchID。请参阅使用此库自动索引您的 CoreData 堆栈的路线实现示例 WACoreDataSpotlight
  • 对于 iOS 9 以及更具体的来说,针对 new iPhones 6S 和 6+S:响应 3D Touch

目录

  1. 故事
  2. 安装和使用
  3. 深入了解

故事

什么激励了我

让我们说实话,GitHub 上有几个路由库可以处理某些已描述的行为。但它们都不符合我的所有要求。所以我带着一些想法写了这个库:

  • 处理控件的 堆栈

如果连返回按钮都没有,或者点击返回按钮会把我带回到打开应用之前的地方,那么打开酒店详情页面的应用就不行了。我只希望应用能打开,这样点击返回时,我能直接回到酒店城市的酒店列表...

  • 不要强迫我使用你的路由匹配器或你的路由处理器。

如果你想自己提供,你应该能够做到。这个最后一点对我来说非常重要。我使用了太多与特定技术紧密相关的库。此外,它们越依赖其实现方式,可测试性就越低。这就是为什么你会看到许多协议都提供了一个默认实现。

  • iOS 9 即将推出(或者当你读到这的时候它可能已经推出了)。iOS 9 带来了这个伟大的功能——通用链接(Universal Links)。嗯,我想有一个干净的方法来处理这项新功能。

灵感来源

从历史角度来看,我最初使用了HHRouter并实现了自己的栈控制器管理。然后,通过重写代码以支持iOS 9,我发现它只是一些没有错误处理的代码,与控制器层次紧密相连,可读性不高,等等。

我决定放弃它,找到更有趣的事情来做。我发现DeepLinkKit并使用了它,直到我意识到它并不符合我的栈需求。所以我重写了一个自定义路由处理器来处理它,并最终得出结论说DeepLinkKit的80%功能已经不再使用了。这就是我决定放弃它并写自己的库的原因。

所以你可能会在两个库的概念中认出一些东西,特别是在路由处理器中,即使实现与DeepLinkKit无关。

安装和使用

与默认实现一起的要求

  • 我提供的实现使用了UINavigationController来处理栈,也可以用于UITabBarController
  • 路由匹配使用:itemID*作为通配符字符。

安装

设置路由:最容易的方法

导入#import <WAAppRouting/WAAppRouting.h>就可以开始了。

你还需要配置一个URL方案(这里我不会详细介绍——外面有大量文档)

导航控制器是一个好的起点

UINavigationController *navigationController = [[UINavigationController alloc] init];

你首先需要分配一个路由匹配器。你可以使用我写的默认实现,或者创建你自己的实现

// Create the default router
self.router = [WAAppRouter defaultRouter];

使用以下语法注册你的路径

  • url_path_component{ClassName}
  • url_path_component1{ClassName1}/url_path_component2{ClassName2}

你可以选择使用!字符触发模态显示。例如:url_path_component{ClassName}/modal{ModalClass}!当调用appscheme://url_path_component/modal时,将ModalClass实例以模态方式显示在导航控制器栈中,并将ClassName放置在导航控制器栈中。

// Register the path
[self.router.registrar
 registerAppRoutePath:
 @"list{WAListViewController}/:itemID{WAListDetailViewController}/extra{WAListDetailExtraViewController}"
 presentingController:navigationController];

如果有需要,添加一个块处理器

// Register some blocks
[self.router.registrar registerBlockRouteHandler:^(WAAppLink *appLink) {
    // Do something every time we are in list/something
}
                                        forRoute:@"list/*"];

最后,将导航控制器设置为根控制器

self.window.rootViewController = navigationController;

现在你可以开始使用了,并用

[self application:(UIApplication *)self openURL:[NSURL URLWithString:@"appscheme://list"] sourceApplication:nil annotation:nil];

不要忘记使用路由器!

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    return [self.router handleURL:url];
}

您使用的每个控制器都应该实现 WAAppRouterTargetControllerProtocol(最好有一个基视图控制器)因此实现此方法,就像魔术般

- (void)reloadFromAppLinkRefresh {
    // You can do something with self.appLink
    // But more important: with self.appLinkRoutingParameters which has merged route|query|default parameters
    NSString *articleTitle = self.appLinkRoutingParameters[@"article_title"];
}

设置路由器:更多控制方法

从最简单的方法开始,但用创建实体来替换“创建路径”

// Create the entities
WAAppRouteEntity *list1Entity =
[WAAppRouteEntity routeEntityWithName:@"list"
                                 path:@"list"
                sourceControllerClass:nil
                targetControllerClass:[WAListViewController class]
                 presentingController:navigationController
             prefersModalPresentation:NO
             defaultParametersBuilder:nil
                    allowedParameters:nil];

WAAppRouteEntity *list1DetailEntity =
[WAAppRouteEntity routeEntityWithName:@"listDetail"
                                 path:@"list/:itemID"
                sourceControllerClass:[WAListViewController class]
                targetControllerClass:[WAListDetailViewController class]
                 presentingController:navigationController
             prefersModalPresentation:NO
             defaultParametersBuilder:^id<WAAppRouterParametersProtocol> {

                 NSMutableDictionary *defaultParameters = [NSMutableDictionary new];
                 defaultParameters[@"defaultParam"]  = @1;
                 defaultParameters[@"defaultParam2"] = @"Default parameter 2";
                 return defaultParameters;
             }
                    allowedParameters:nil];

将实体添加到注册器

// Register the entities
[self.router.registrar registerAppRouteEntity:list1Entity];
[self.router.registrar registerAppRouteEntity:list1DetailEntity];

示例

有四个示例可用

  • SimpleExample:这是一个样本,处理列表、详情和额外内容。这可以看作是文章列表、其详情和评论。
  • SimpleExampleParameters:这个样本与SimpleExample相同,但使用的是WAAppLinkParameters(这个库的另一个重要部分)。
  • MoreComplexExample:这个样本演示了如何处理标签栏控制器+如何处理模态窗口。
  • PPRevealSample:这个样本作为演示,通过一点努力,自定义容器可以适应路由库吗?

文档

代码有充分的文档说明,你应该能找到你所有的问题的答案。否则,打开一个问题,我会尽快回答。

深入

以对象预先配置所有控制器。

您可能希望在控制器被分配时传入值。例如,在我参与的一个项目中,我们有一个图像缓存,控制器需要显示图像。这个图像缓存是由应用委托器分配的(为了避免单例并使代码更易于测试)。为了做到这一点,您需要向路由处理程序添加一个块实现

    [routeHandler setControllerPreConfigurationBlock:^(UIViewController *controller, WAAppRouteEntity *routeEntity, WAAppLink *appLink) {
        if ([controller isKindOfClass:[WABaseViewController class]]) {
            ((WABaseViewController *)controller).imageCache = imageCache;
        }
    }];

禁止显示特定实体

您可以通过设置此块来请求在运行时不要处理某些路由(例如,您可能希望在未登录时不要显示某些控制器)

    [routeHandler setShouldHandleAppLinkBlock:^BOOL(WAAppRouteEntity *entity) {
        // Could return NO if not logged in for example
        return YES;
    }];

通配符URL

您可以使用通配符URL,例如list/*/extra,意味着对于任何值,而不是*,都将执行实体或块。避免与实体一起使用,而应与块一起使用。形式为list/*的URL将匹配list/pathlist/path/extra

以下是一个每当我们在list/之后时触发的警告示例。

[registrar registerBlockRouteHandler:^(WAAppLink *appLink) {
        [RZNotificationView showNotificationOn:RZNotificationContextAboveStatusBar
                                       message:[NSString stringWithFormat:@"You are dealing with item ID %@", appLink[@"articleID"]]
                                          icon:RZNotificationIconInfo
                                        anchor:RZNotificationAnchorNone
                                      position:RZNotificationPositionTop
                                         color:RZNotificationColorYellow
                                    assetColor:RZNotificationContentColorDark
                                     textColor:RZNotificationContentColorDark
                                withCompletion:^(BOOL touched) {

                                }];
    }
                                forRoute:@"list/*"];

自定义路由器行为

如前所述,我讨厌那些不能自定义而不分叉和偏离原始源的库。因此,您可以通过两种方式来自定义路由器:自定义路由匹配器和自定义路由处理程序。

自定义路由匹配器

我的实现只处理基本用法。这意味着它不会支持查询中的key=value1, value2等。它也是大小写敏感的。如果您有自己的URL配置,如list/$itemID实现一个新的路由匹配器是个好主意!

开始时,阅读WAAppRouteMatcherProtocol类。您需要实现两个方法:matchesURL: fromPathPattern:parametersFromURL: withPathPattern:。正如您在我的实现中可以看到的那样,我正在使用WARoutePattern来匹配URL。这在某种程度上受到了SocKit的启发(对于命名约定)。

然后,你可以轻松创建路由器使用

// Allocate your route matcher
MyRouteMatcher *routeMatcher = [MyRouteMatcher new];

// Create the Registrar
WAAppRouteRegistrar *registrar  = [WAAppRouteRegistrar registrarWithRouteMatcher:routeMatcher];

// Create the route handler
WAAppRouteHandler *routeHandler = [WAAppRouteHandler routeHandlerWithRouteRegistrar:registrar];

// Create the router
WAAppRouter *router = [WAAppRouter routerWithRegistrar:registrar
                                          routeHandler:routeHandler];

自定义路由处理器

例如,如果你想不处理堆栈,或者使用除 UINavigationController 之外的内容,那么考虑创建你自己的路由处理器。首先采用 WAAppRouteHandlerProtocol 协议。然后阅读 WAAppRouteHandler 获取灵感。

iOS 9 支持

我仍然需要运行一些测试,但是想法是有一个用于经典 URL 方案的路由器,还有一个用于通用链接的路由器。

3D Touch

通过实现 3D Touch,用户可以直接在某些操作上打开您的应用,例如 新推文搜索推文获取方向 等。您只需遵循 UIApplicationShortcutItem 的文档 (在此处) 并执行以下操作:

- (void)application:(UIApplication * _Nonnull)application
performActionForShortcutItem:(UIApplicationShortcutItem * _Nonnull)shortcutItem
  completionHandler:(void (^ _Nonnull)(BOOL succeeded))completionHandler {
    if ([shortcutItem.type isEqualToString:@"newTweet"]) {
        // goto://home/newTweet
    }
}

搜索

例如使用 WACoreDataSpotlight(示例使用 WAppRouting),您可以对 从搜索项目中打开应用 事件做出响应。

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
    NSManagedObject *object = [self.mainIndexer objectFromUserActivity:userActivity];

    if ([object isKindOfClass:[Company class]]) {
        [AppLink goTo:@"companies/%@", ((Company *)object).name];
    }

    return YES;
} 

特殊配置注意事项

自定义容器控制器

您绝对应该查看 PPRevealSample 项目,以了解如何完成这项工作。如果您需要帮助,我也在这里。基本思路是尝试分配您需要的所有导航控制器并将它们传递为显示控制器。然后,它将表现得像标签栏(请参阅其类别)。如果没有,那么您处于 PPRevealSideViewController 上下文,其中导航控制器会动态分配。思路是将容器作为 presentingController 属性传递并实现 WAAppRoutingContainerPresentationProtocol 协议(您还需要可用的方法)。

带有导航控制器的模态

您还不能(尚不能)呈现一个模态控制器然后推送一个详情控制器。就像呈现一个登录视图,但推送到注册控制器上。

在不同的位置重用控制器

因为堆栈检索是以处理控制器类唯一性来实现,所以当源控制器类不为 nil 时,您不能有两个或以上的 WAAppRouteEntity 具有相同的目标类。如果您需要在不同的地方重用控制器,请考虑创建一个主控制器的主子类,该主控制器处理所有行为。

还有一点:避免硬编码参数键

目的

这个库还有一点是 WAAppLinkParameters 类。其背后的想法是避免硬编码值。行为基于对 WAAppRouterParametersProtocol 协议的实现,这意味着您可以提供自己的或从 WAAppLinkParameters 继承,它提供了默认行为并实现所有协议方法。让我们看一下在 SimpleExampleParameters 示例项目中找到的示例。

示例

首先创建一个子类

@interface ArticleAppLinkParameters : WAAppLinkParameters

@property (nonatomic, strong) NSNumber *articleID;
@property (nonatomic, strong) NSString *articleTitle;
@property (nonatomic, strong) NSNumber *displayType;

@end

在这里,您可以看到应该映射到URL键的三个对象。您需要重写mappingKeyProperty获取器以提供映射url_key: object_property

- (NSDictionary *)mappingKeyProperty {
    return @{
             @"articleID": @"articleID",
             @"article_title": @"articleTitle",
             @"display_type": @"displayType"
             };
}

我编写了一个关于UIViewController的类别,用于为您合并对象进行配置。因此,现在您可以直接获取值

self.label.text = [NSString stringWithFormat:@"ArticleID: %@", ((ArticleAppLinkParameters *)self.appLinkRoutingParameters).articleID];`

您可以复制参数,并将值设置为将来使用

ArticleAppLinkParameters *params = [(ArticleAppLinkParameters *)self.appLinkRoutingParameters copy];
params.articleTitle = [NSString stringWithFormat:@"My super article %ld", (long)indexPath.row];

使用白名单获取查询

NSString *query = [params queryStringWithWhiteList:@[@"articleID", @"articleTitle", @"displayType"];

优势

  • 无需修改代码即可随时更新URL上的键。
  • 避免在URL中添加新参数时回归。
  • 轻松构建从控制器到控制器的查询。
  • 为主控制器初始化提供默认值。所有配置都在一个地方完成。

备注

  • 目前只支持NSStringNSNumber参数(例如没有NSDate
  • 这听起来可能不如直接使用参数那么令人头疼。诚然,但请记住,我在涉及大量维护和演化的一个大项目中考虑了这一点。

贡献:问题、建议、拉取请求?

如果您遇到特定于WAAppRouting的问题,请在此处创建新的问题

鼓励新的功能拉取请求,并且非常感激!请尽最大努力保持与现有代码风格的一致性。如果您正在考虑进行重大更改或添加到项目中,请先通过打开新的问题与我联系,以便有机会进行合并。

就这样

  • 如果您满意,请随时通过@ipodishima发推文给我!
  • 在MIT许可下分发。
  • facebook上关注Wasappli