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 1 commit
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
6 changes: 6 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,6 +69,8 @@
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; };
Expand Down Expand Up @@ -375,6 +378,8 @@
isa = PBXGroup;
children = (
DE16E9271A42883B00077E18 /* DPLDeepLinkRouter_Private.h */,
58A6E375439CCD263A6F3A86 /* DPLTestRouteHandler.m */,
58A6EE2855B2425CC971B8B2 /* DPLTestRouteHandler.h */,
);
path = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -844,6 +849,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 ;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL should be uppercase here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok! Will do that for every URL in the code.


///--------------------
/// @name Configuration
Expand Down
91 changes: 63 additions & 28 deletions DeepLinkKit/Router/DPLDeepLinkRouter.m
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,17 @@ - (BOOL)handleURL:(NSURL *)url withCompletion:(DPLRouteCompletionBlock)completio
return NO;
}

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;
}
}

id handler;
deepLink = [self deepLinkForUrl:url handler:&handler];

NSError *error;
BOOL isHandled = NO;
if (!deepLink) {
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:handler withDeepLink:deepLink error:&error];
}

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

- (UIViewController <DPLTargetViewController> *)viewControllerForUrl:(NSURL *)url {
DPLDeepLink *deepLink;
id handler;
deepLink = [self deepLinkForUrl:url handler:&handler];
if (handler) {
DPLRouteHandler *routeHandler = [self routeHandlerForHandler:handler];
if (routeHandler) {
return [self viewControllerForHandler:routeHandler withDeepLink: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);

- (DPLDeepLink *)deepLinkForUrl:(NSURL *)url handler:(id *)handler {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that an inout parameter isn't ideal here. Maybe this method could return a dictionary? Or we could even create a lightweight class that had deepLink and handler properties.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I almost created that lightweight class. IMO is the best option since we don't have tuples in Objective C. :)

DPLDeepLink *deepLink;
for (NSString *route in self.routes) {
DPLRouteMatcher *matcher = [DPLRouteMatcher matcherWithRoute:route];
deepLink = [matcher deepLinkWithURL:url];
if (deepLink) {
*handler = self[route];
break;
}
}
else if (class_isMetaClass(object_getClass(handler)) &&
[handler isSubclassOfClass:[DPLRouteHandler class]]) {
DPLRouteHandler *routeHandler = [[handler alloc] init];
return deepLink;
}


- (BOOL)handleRoute:(id)handler withDeepLink:(DPLDeepLink *)deepLink error:(NSError *__autoreleasing *)error {
DPLRouteHandler *routeHandler = [self routeHandlerForHandler: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 ([handler isKindOfClass:NSClassFromString(@"NSBlock")]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might cause a problem in the future if the private class name changes. Can we just create a throwaway DPLRouteHandlerBlock and compare its class to handler's?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could certainly do that. That line is actually a copy from what was already in place on the code. I just had to move it to extract things from the methods.

But from what I've seen, we can have two kinds of blocks, with and without a DPLDeepLink argument. So maybe that's why it was this way in the first place?

DPLRouteHandlerBlock routeHandlerBlock = 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
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