Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: internal router #61

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions DeepLinkKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
1252340A196B4771BE27D5FD /* libPods-ReceiverDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CFE63F7FEA51182807D98A78 /* libPods-ReceiverDemo.a */; };
2F4988DE1AE71ABC0069EF2B /* DPLRouteHandlerIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 2F4988DD1AE71ABC0069EF2B /* DPLRouteHandlerIntegrationTest.m */; };
4D4F412B1B02A96400B710DB /* DPLRegularExpressionSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D4F412A1B02A96400B710DB /* DPLRegularExpressionSpec.m */; };
58A6E3A41A6F6DED8E88295C /* DPLTestRouteHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 58A6E375439CCD263A6F3A86 /* DPLTestRouteHandler.m */; };
6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; };
6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
Expand Down Expand Up @@ -68,11 +69,15 @@
2F4988DD1AE71ABC0069EF2B /* DPLRouteHandlerIntegrationTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DPLRouteHandlerIntegrationTest.m; path = IntegrationTests/DPLRouteHandlerIntegrationTest.m; sourceTree = "<group>"; };
4D4F412A1B02A96400B710DB /* DPLRegularExpressionSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DPLRegularExpressionSpec.m; sourceTree = "<group>"; };
57D5F02E049D7887B4F4ACDF /* Pods-ReceiverDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReceiverDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ReceiverDemo/Pods-ReceiverDemo.debug.xcconfig"; sourceTree = "<group>"; };
58A6E375439CCD263A6F3A86 /* DPLTestRouteHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DPLTestRouteHandler.m; sourceTree = "<group>"; };
58A6EE2855B2425CC971B8B2 /* DPLTestRouteHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DPLTestRouteHandler.h; sourceTree = "<group>"; };
6003F58A195388D20070C39A /* ReceiverDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReceiverDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
6003F5AE195388D20070C39A /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
65F47C441B4CA335003A3228 /* DPLMatchedRoute.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DPLMatchedRoute.h; sourceTree = "<group>"; };
65F47C451B4CA552003A3228 /* DPLMatchedRoute.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DPLMatchedRoute.m; sourceTree = "<group>"; };
6B9E60301031FFD1833ECA7A /* Pods-SenderDemo.test.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SenderDemo.test.xcconfig"; path = "Pods/Target Support Files/Pods-SenderDemo/Pods-SenderDemo.test.xcconfig"; sourceTree = "<group>"; };
81A53B1FA1F6DF1D2B557DCD /* libPods-SenderDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SenderDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; };
83D34C3B1B03ECAD00BA6EF1 /* DPLMatchResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DPLMatchResult.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -358,6 +363,8 @@
children = (
DE16E9201A42882F00077E18 /* DPLDeepLinkRouter.h */,
DE16E9211A42882F00077E18 /* DPLDeepLinkRouter.m */,
65F47C441B4CA335003A3228 /* DPLMatchedRoute.h */,
65F47C451B4CA552003A3228 /* DPLMatchedRoute.m */,
);
path = Router;
sourceTree = "<group>";
Expand All @@ -375,6 +382,8 @@
isa = PBXGroup;
children = (
DE16E9271A42883B00077E18 /* DPLDeepLinkRouter_Private.h */,
58A6E375439CCD263A6F3A86 /* DPLTestRouteHandler.m */,
58A6EE2855B2425CC971B8B2 /* DPLTestRouteHandler.h */,
);
path = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -844,6 +853,7 @@
DEA55ADF1A5F2BBC00C312F5 /* DPLDeepLink_AppLinksSpec.m in Sources */,
DE3E610F1A3B4492008D6DFC /* NSString_DPLJSONSpec.m in Sources */,
DE16E91E1A42733100077E18 /* NSString_DPLTrimSpec.m in Sources */,
58A6E3A41A6F6DED8E88295C /* DPLTestRouteHandler.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
8 changes: 8 additions & 0 deletions DeepLinkKit/Router/DPLDeepLinkRouter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

@class DPLDeepLink;
@protocol DPLRouteHandler;
@class UIViewController;
@protocol DPLTargetViewController;


/**
Expand Down Expand Up @@ -96,6 +98,12 @@ typedef void(^DPLRouteCompletionBlock)(BOOL handled, NSError *error);
*/
- (BOOL)handleUserActivity:(NSUserActivity *)userActivity withCompletion:(DPLRouteCompletionBlock)completionHandler;

/**
* Attempts to find a `UIViewController' for incoming URL. The URL must have been registered with a `DPLRouteHandler'.
* @param url The incoming URL from `application:openURL:sourceApplication:annotation:'
* @return A view controller if successful, already configured, otherwise Nil.
*/
- (UIViewController<DPLTargetViewController> *)viewControllerForURL:(NSURL *)url ;

///--------------------
/// @name Configuration
Expand Down
90 changes: 61 additions & 29 deletions DeepLinkKit/Router/DPLDeepLinkRouter.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "DPLDeepLinkRouter.h"
#import "DPLMatchedRoute.h"
#import "DPLRouteMatcher.h"
#import "DPLDeepLink.h"
#import "DPLRouteHandler.h"
Expand Down Expand Up @@ -116,21 +117,15 @@ - (BOOL)handleURL:(NSURL *)url withCompletion:(DPLRouteCompletionBlock)completio
return NO;
}

DPLMatchedRoute *matchedRoute = [self deepLinkForURL:url];

NSError *error;
DPLDeepLink *deepLink;
__block BOOL isHandled = NO;
for (NSString *route in self.routes) {
DPLRouteMatcher *matcher = [DPLRouteMatcher matcherWithRoute:route];
deepLink = [matcher deepLinkWithURL:url];
if (deepLink) {
isHandled = [self handleRoute:route withDeepLink:deepLink error:&error];
break;
}
}

if (!deepLink) {
BOOL isHandled = NO;
if (!matchedRoute) {
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"The passed URL does not match a registered route.", nil) };
error = [NSError errorWithDomain:DPLErrorDomain code:DPLRouteNotFoundError userInfo:userInfo];
} else {
isHandled = [self handleRoute:matchedRoute error:&error];
}

[self completeRouteWithSuccess:isHandled error:error];
Expand All @@ -147,45 +142,82 @@ - (BOOL)handleUserActivity:(NSUserActivity *)userActivity withCompletion:(DPLRou
return NO;
}

- (UIViewController <DPLTargetViewController> *)viewControllerForURL:(NSURL *)url {
DPLMatchedRoute *matchedRoute = [self deepLinkForURL:url];
if (matchedRoute) {
DPLRouteHandler *routeHandler = [self routeHandlerForHandler:matchedRoute.handler];
if (routeHandler) {
return [self viewControllerForHandler:routeHandler withDeepLink:matchedRoute.deepLink];
}
}
return nil;
}


- (BOOL)handleRoute:(NSString *)route withDeepLink:(DPLDeepLink *)deepLink error:(NSError *__autoreleasing *)error {
id handler = self[route];

if ([handler isKindOfClass:NSClassFromString(@"NSBlock")]) {
DPLRouteHandlerBlock routeHandlerBlock = handler;
routeHandlerBlock(deepLink);
- (DPLMatchedRoute *)deepLinkForURL:(NSURL *)url {
DPLMatchedRoute *matched;
for (NSString *route in self.routes) {
DPLRouteMatcher *matcher = [DPLRouteMatcher matcherWithRoute:route];
DPLDeepLink *deepLink = [matcher deepLinkWithURL:url];
if (deepLink) {
matched = [DPLMatchedRoute routeWithDeepLink:deepLink handler:self[route]];
break;
}
}
else if (class_isMetaClass(object_getClass(handler)) &&
[handler isSubclassOfClass:[DPLRouteHandler class]]) {
DPLRouteHandler *routeHandler = [[handler alloc] init];
return matched;
}

- (BOOL)handleRoute:(DPLMatchedRoute *)matchedRoute error:(NSError *__autoreleasing *)error {
DPLDeepLink *deepLink = matchedRoute.deepLink;
DPLRouteHandler *routeHandler = [self routeHandlerForHandler:matchedRoute.handler];
if (routeHandler) {
if (![routeHandler shouldHandleDeepLink:deepLink]) {
return NO;
}

UIViewController *presentingViewController = [routeHandler viewControllerForPresentingDeepLink:deepLink];
UIViewController <DPLTargetViewController> *targetViewController = [routeHandler targetViewController];
UIViewController <DPLTargetViewController> *targetViewController = [self viewControllerForHandler:routeHandler withDeepLink:deepLink];

if (targetViewController) {
[targetViewController configureWithDeepLink:deepLink];
[routeHandler presentTargetViewController:targetViewController inViewController:presentingViewController];
}
else {
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: NSLocalizedString(@"The matched route handler does not specify a target view controller.", nil)};

NSDictionary *userInfo = @{NSLocalizedDescriptionKey : NSLocalizedString(@"The matched route handler does not specify a target view controller.", nil)};

if (error) {
*error = [NSError errorWithDomain:DPLErrorDomain code:DPLRouteHandlerTargetNotSpecifiedError userInfo:userInfo];
}

return NO;
}
}

else if ([matchedRoute.handler isKindOfClass:NSClassFromString(@"NSBlock")]) {
DPLRouteHandlerBlock routeHandlerBlock = matchedRoute.handler;
routeHandlerBlock(deepLink);
}

return YES;
}


- (DPLRouteHandler *)routeHandlerForHandler:(id)handler {
if (class_isMetaClass(object_getClass(handler)) &&
[handler isSubclassOfClass:[DPLRouteHandler class]]) {
return [[handler alloc] init];
}
return nil;
}


- (UIViewController <DPLTargetViewController> *)viewControllerForHandler:(DPLRouteHandler *)routeHandler withDeepLink:(DPLDeepLink *)deepLink {
UIViewController <DPLTargetViewController> *targetViewController;
targetViewController = [routeHandler targetViewController];
[targetViewController configureWithDeepLink:deepLink];
return targetViewController;
}


- (void)completeRouteWithSuccess:(BOOL)handled error:(NSError *)error {

dispatch_async(dispatch_get_main_queue(), ^{
Expand Down
13 changes: 13 additions & 0 deletions DeepLinkKit/Router/DPLMatchedRoute.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@class DPLDeepLink;

@interface DPLMatchedRoute : NSObject

@property (nonatomic, strong) DPLDeepLink *deepLink;
@property (nonatomic, strong) id handler;

- (instancetype)initWithDeepLink:(DPLDeepLink *)deepLink handler:(id)handler;

+ (instancetype)routeWithDeepLink:(DPLDeepLink *)deepLink handler:(id)handler;


@end
19 changes: 19 additions & 0 deletions DeepLinkKit/Router/DPLMatchedRoute.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#import "DPLMatchedRoute.h"

@implementation DPLMatchedRoute

- (instancetype)initWithDeepLink:(DPLDeepLink *)deepLink handler:(id)handler {
self = [super init];
if (self) {
self.deepLink = deepLink;
self.handler = handler;
}

return self;
}

+ (instancetype)routeWithDeepLink:(DPLDeepLink *)deepLink handler:(id)handler {
return [[self alloc] initWithDeepLink:deepLink handler:handler];
}

@end
9 changes: 9 additions & 0 deletions Tests/Router/DPLDeepLinkRouterSpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#import "DPLRouteHandler.h"
#import "DPLDeepLink.h"
#import "DPLErrors.h"
#import "DPLTestRouteHandler.h"

SpecBegin(DPLDeepLinkRouter)

Expand Down Expand Up @@ -201,6 +202,14 @@
BOOL isHandled = [router handleUserActivity:activity withCompletion:NULL];
expect(isHandled).to.beFalsy();
});

it(@"returns a UIViewController when a route handler is registered", ^{
router[@"/say/:word"] = [DPLTestRouteHandler class];

UIViewController *vc = [router viewControllerForURL:url];
expect(vc).toNot.beNil();
expect(vc).to.beAKindOf([TestViewController class]);
});
});

SpecEnd
7 changes: 7 additions & 0 deletions Tests/Router/Helpers/DPLTestRouteHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@class DPLRouteHandler;

@interface DPLTestRouteHandler : DPLRouteHandler
@end

@interface TestViewController : UIViewController<DPLTargetViewController>
@end
13 changes: 13 additions & 0 deletions Tests/Router/Helpers/DPLTestRouteHandler.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#import "DPLRouteHandler.h"
#import "DPLTestRouteHandler.h"

@implementation DPLTestRouteHandler
- (UIViewController <DPLTargetViewController> *)targetViewController {
return [[TestViewController alloc] init];
}
@end

@implementation TestViewController
- (void)configureWithDeepLink:(DPLDeepLink *)deepLink {}
@end