├── MGJRouterDemo ├── MGJRouterDemo │ ├── DemoDetailViewController.h │ ├── AppDelegate.h │ ├── main.m │ ├── DemoListViewController.h │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── DemoListViewController.m │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ └── DemoDetailViewController.m ├── MGJRouterDemoTests │ ├── Info.plist │ └── MGJRouterDemoTests.m └── MGJRouterDemo.xcodeproj │ └── project.pbxproj ├── .gitignore ├── LICENSE ├── MGJRouter ├── MGJRouter.h └── MGJRouter.m ├── README.md └── MGJRouter.podspec /MGJRouterDemo/MGJRouterDemo/DemoDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoDetailViewController.h 3 | // MGJRequestManagerDemo 4 | // 5 | // Created by limboy on 3/20/15. 6 | // Copyright (c) 2015 juangua. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DemoDetailViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | # Pods - for those of you who use CocoaPods 20 | Pods 21 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MGJRouterDemo 4 | // 5 | // Created by limboy on 5/27/15. 6 | // Copyright (c) 2015 juangua. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MGJRouterDemo 4 | // 5 | // Created by limboy on 5/27/15. 6 | // Copyright (c) 2015 juangua. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo/DemoListViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoListViewController.h 3 | // MGJRequestManagerDemo 4 | // 5 | // Created by limboy on 3/20/15. 6 | // Copyright (c) 2015 juangua. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef UIViewController *(^ViewControllerHandler)(); 12 | 13 | @interface DemoListViewController : UIViewController 14 | 15 | + (void)registerWithTitle:(NSString *)title handler:(ViewControllerHandler)handler; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.mogujie.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemoTests/MGJRouterDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MGJRouterDemoTests.m 3 | // MGJRouterDemoTests 4 | // 5 | // Created by limboy on 5/27/15. 6 | // Copyright (c) 2015 juangua. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface MGJRouterDemoTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation MGJRouterDemoTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 mogujie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.mogujie.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo/DemoListViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoListViewController.m 3 | // MGJRequestManagerDemo 4 | // 5 | // Created by limboy on 3/20/15. 6 | // Copyright (c) 2015 juangua. All rights reserved. 7 | // 8 | 9 | #import "DemoListViewController.h" 10 | 11 | static NSMutableDictionary *titleWithHandlers; 12 | static NSMutableArray *titles; 13 | 14 | @interface DemoListViewController () 15 | @property (nonatomic) UITableView *tableView; 16 | @end 17 | 18 | @implementation DemoListViewController 19 | 20 | + (void)registerWithTitle:(NSString *)title handler:(UIViewController *(^)())handler 21 | { 22 | if (!titleWithHandlers) { 23 | titleWithHandlers = [[NSMutableDictionary alloc] init]; 24 | titles = [NSMutableArray array]; 25 | } 26 | [titles addObject:title]; 27 | titleWithHandlers[title] = handler; 28 | } 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | // Do any additional setup after loading the view. 33 | self.title = @"MGJRouterDemo"; 34 | 35 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; 36 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; 37 | self.tableView.delegate = self; 38 | self.tableView.dataSource = self; 39 | [self.view addSubview:self.tableView]; 40 | } 41 | 42 | - (void)didReceiveMemoryWarning { 43 | [super didReceiveMemoryWarning]; 44 | // Dispose of any resources that can be recreated. 45 | } 46 | 47 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 48 | { 49 | return titleWithHandlers.allKeys.count; 50 | } 51 | 52 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 53 | { 54 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; 55 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 56 | 57 | cell.textLabel.text = titles[indexPath.row]; 58 | return cell; 59 | } 60 | 61 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 62 | { 63 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 64 | UIViewController *viewController = ((ViewControllerHandler)titleWithHandlers[titles[indexPath.row]])(); 65 | [self.navigationController pushViewController:viewController animated:YES]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MGJRouterDemo 4 | // 5 | // Created by limboy on 5/27/15. 6 | // Copyright (c) 2015 juangua. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "DemoListViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 22 | [self.window makeKeyAndVisible]; 23 | 24 | DemoListViewController *vc = [[DemoListViewController alloc] init]; 25 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:vc]; 26 | self.window.rootViewController = navigationController; 27 | 28 | return YES; 29 | } 30 | 31 | - (void)applicationWillResignActive:(UIApplication *)application { 32 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 33 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 34 | } 35 | 36 | - (void)applicationDidEnterBackground:(UIApplication *)application { 37 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 38 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 39 | } 40 | 41 | - (void)applicationWillEnterForeground:(UIApplication *)application { 42 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 43 | } 44 | 45 | - (void)applicationDidBecomeActive:(UIApplication *)application { 46 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 47 | } 48 | 49 | - (void)applicationWillTerminate:(UIApplication *)application { 50 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /MGJRouter/MGJRouter.h: -------------------------------------------------------------------------------- 1 | // 2 | // MGJRouter.h 3 | // MGJFoundation 4 | // 5 | // Created by limboy on 12/9/14. 6 | // Copyright (c) 2014 juangua. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString *const MGJRouterParameterURL; 12 | extern NSString *const MGJRouterParameterCompletion; 13 | extern NSString *const MGJRouterParameterUserInfo; 14 | 15 | /** 16 | * routerParameters 里内置的几个参数会用到上面定义的 string 17 | */ 18 | typedef void (^MGJRouterHandler)(NSDictionary *routerParameters); 19 | 20 | /** 21 | * 需要返回一个 object,配合 objectForURL: 使用 22 | */ 23 | typedef id (^MGJRouterObjectHandler)(NSDictionary *routerParameters); 24 | 25 | @interface MGJRouter : NSObject 26 | 27 | /** 28 | * 注册 URLPattern 对应的 Handler,在 handler 中可以初始化 VC,然后对 VC 做各种操作 29 | * 30 | * @param URLPattern 带上 scheme,如 mgj://beauty/:id 31 | * @param handler 该 block 会传一个字典,包含了注册的 URL 中对应的变量。 32 | * 假如注册的 URL 为 mgj://beauty/:id 那么,就会传一个 @{@"id": 4} 这样的字典过来 33 | */ 34 | + (void)registerURLPattern:(NSString *)URLPattern toHandler:(MGJRouterHandler)handler; 35 | 36 | /** 37 | * 注册 URLPattern 对应的 ObjectHandler,需要返回一个 object 给调用方 38 | * 39 | * @param URLPattern 带上 scheme,如 mgj://beauty/:id 40 | * @param handler 该 block 会传一个字典,包含了注册的 URL 中对应的变量。 41 | * 假如注册的 URL 为 mgj://beauty/:id 那么,就会传一个 @{@"id": 4} 这样的字典过来 42 | * 自带的 key 为 @"url" 和 @"completion" (如果有的话) 43 | */ 44 | + (void)registerURLPattern:(NSString *)URLPattern toObjectHandler:(MGJRouterObjectHandler)handler; 45 | 46 | /** 47 | * 取消注册某个 URL Pattern 48 | * 49 | * @param URLPattern 50 | */ 51 | + (void)deregisterURLPattern:(NSString *)URLPattern; 52 | 53 | /** 54 | * 打开此 URL 55 | * 会在已注册的 URL -> Handler 中寻找,如果找到,则执行 Handler 56 | * 57 | * @param URL 带 Scheme,如 mgj://beauty/3 58 | */ 59 | + (void)openURL:(NSString *)URL; 60 | 61 | /** 62 | * 打开此 URL,同时当操作完成时,执行额外的代码 63 | * 64 | * @param URL 带 Scheme 的 URL,如 mgj://beauty/4 65 | * @param completion URL 处理完成后的 callback,完成的判定跟具体的业务相关 66 | */ 67 | + (void)openURL:(NSString *)URL completion:(void (^)(id result))completion; 68 | 69 | /** 70 | * 打开此 URL,带上附加信息,同时当操作完成时,执行额外的代码 71 | * 72 | * @param URL 带 Scheme 的 URL,如 mgj://beauty/4 73 | * @param parameters 附加参数 74 | * @param completion URL 处理完成后的 callback,完成的判定跟具体的业务相关 75 | */ 76 | + (void)openURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo completion:(void (^)(id result))completion; 77 | 78 | /** 79 | * 查找谁对某个 URL 感兴趣,如果有的话,返回一个 object 80 | * 81 | * @param URL 82 | */ 83 | + (id)objectForURL:(NSString *)URL; 84 | 85 | /** 86 | * 查找谁对某个 URL 感兴趣,如果有的话,返回一个 object 87 | * 88 | * @param URL 89 | * @param userInfo 90 | */ 91 | + (id)objectForURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo; 92 | 93 | /** 94 | * 是否可以打开URL 95 | * 96 | * @param URL 97 | * 98 | * @return 99 | */ 100 | + (BOOL)canOpenURL:(NSString *)URL; 101 | 102 | /** 103 | * 调用此方法来拼接 urlpattern 和 parameters 104 | * 105 | * #define MGJ_ROUTE_BEAUTY @"beauty/:id" 106 | * [MGJRouter generateURLWithPattern:MGJ_ROUTE_BEAUTY, @[@13]]; 107 | * 108 | * 109 | * @param pattern url pattern 比如 @"beauty/:id" 110 | * @param parameters 一个数组,数量要跟 pattern 里的变量一致 111 | * 112 | * @return 113 | */ 114 | + (NSString *)generateURLWithPattern:(NSString *)pattern parameters:(NSArray *)parameters; 115 | @end 116 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MGJRouter 2 | 一个高效/灵活的 iOS URL Router 3 | 4 | ### 2015-08-22 更新 5 | 6 | #### 添加了同步获取 Object 的方法 7 | 8 | 有些场景我们可能需要根据 URL 获取某个 Object,所以就添加了这个方法 9 | 10 | ```objc 11 | UIView *searchTopBar = [MGJRouter objectForURL:@"search_top_bar"]; 12 | ``` 13 | 14 | ## 为什么要再造一个轮子? 15 | 已经有几款不错的 Router 了,如 [JLRoutes](https://github.com/joeldev/JLRoutes), [HHRouter](https://github.com/Huohua/HHRouter), 但细看了下之后发现,还是不太满足需求。 16 | 17 | JLRoutes 的问题主要在于查找 URL 的实现不够高效,通过遍历而不是匹配。还有就是功能偏多。 18 | 19 | HHRouter 的 URL 查找是基于匹配,所以会更高效,MGJRouter 也是采用的这种方法,但它跟 ViewController 绑定地过于紧密,一定程度上降低了灵活性。 20 | 21 | 于是就有了 MGJRouter。 22 | 23 | ## 安装 24 | 25 | ``` 26 | pod 'MGJRouter', '~>0.9.0' 27 | ``` 28 | 29 | ## 使用姿势 30 | 31 | ### 最基本的使用 32 | 33 | ```objc 34 | [MGJRouter registerURLPattern:@"mgj://foo/bar" toHandler:^(NSDictionary *routerParameters) { 35 | NSLog(@"routerParameterUserInfo:%@", routerParameters[MGJRouterParameterUserInfo]); 36 | }]; 37 | 38 | [MGJRouter openURL:@"mgj://foo/bar"]; 39 | ``` 40 | 41 | 当匹配到 URL 后,`routerParameters` 会自带几个 key 42 | 43 | ```objc 44 | extern NSString *const MGJRouterParameterURL; 45 | extern NSString *const MGJRouterParameterCompletion; 46 | extern NSString *const MGJRouterParameterUserInfo; 47 | ``` 48 | 49 | ### 处理中文也没有问题 50 | 51 | ```objc 52 | [MGJRouter registerURLPattern:@"mgj://category/家居" toHandler:^(NSDictionary *routerParameters) { 53 | NSLog(@"routerParameters:%@", routerParameters); 54 | }]; 55 | 56 | [MGJRouter openURL:@"mgj://category/家居"]; 57 | ``` 58 | 59 | ### Open 时,可以传一些 userinfo 过去 60 | 61 | ```objc 62 | [MGJRouter registerURLPattern:@"mgj://category/travel" toHandler:^(NSDictionary *routerParameters) { 63 | NSLog(@"routerParameters[MGJRouterParameterUserInfo]:%@", routerParameters[MGJRouterParameterUserInfo]); 64 | // @{@"user_id": @1900} 65 | }]; 66 | 67 | [MGJRouter openURL:@"mgj://category/travel" withUserInfo:@{@"user_id": @1900} completion:nil]; 68 | ``` 69 | 70 | ### 如果有可变参数(包括 URL Query Parameter)会被自动解析 71 | 72 | ```objc 73 | [MGJRouter registerURLPattern:@"mgj://search/:query" toHandler:^(NSDictionary *routerParameters) { 74 | NSLog(@"routerParameters[query]:%@", routerParameters[@"query"]); // bicycle 75 | NSLog(@"routerParameters[color]:%@", routerParameters[@"color"]); // red 76 | }]; 77 | 78 | [MGJRouter openURL:@"mgj://search/bicycle?color=red"]; 79 | ``` 80 | 81 | ### 定义一个全局的 URL Pattern 作为 Fallback 82 | 83 | ```objc 84 | [MGJRouter registerURLPattern:@"mgj://" toHandler:^(NSDictionary *routerParameters) { 85 | NSLog(@"没有人处理该 URL,就只能 fallback 到这里了"); 86 | }]; 87 | 88 | [MGJRouter openURL:@"mgj://search/travel/china?has_travelled=0"]; 89 | ``` 90 | 91 | ### 当 Open 结束时,执行 Completion Block 92 | 93 | ```objc 94 | [MGJRouter registerURLPattern:@"mgj://detail" toHandler:^(NSDictionary *routerParameters) { 95 | NSLog(@"匹配到了 url, 一会会执行 Completion Block"); 96 | 97 | // 模拟 push 一个 VC 98 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 99 | void (^completion)() = routerParameters[MGJRouterParameterCompletion]; 100 | if (completion) { 101 | completion(); 102 | } 103 | }); 104 | }]; 105 | 106 | [MGJRouter openURL:@"mgj://detail" withUserInfo:nil completion:^{ 107 | [self appendLog:@"Open 结束,我是 Completion Block"]; 108 | }]; 109 | ``` 110 | 111 | ### 生成 URL 112 | 113 | URL 的处理一不小心,就容易散落在项目的各个角落,不容易管理。比如注册时的 pattern 是 `mgj://beauty/:id`,然后 open 时就是 `mgj://beauty/123`,这样到时候 url 有改动,处理起来就会很麻烦,不好统一管理。 114 | 115 | 所以 MGJRouter 提供了一个类方法来处理这个问题。 116 | 117 | ```objc 118 | + (NSString *)generateURLWithPattern:(NSString *)pattern parameters:(NSArray *)parameters; 119 | ``` 120 | 121 | 使用方式 122 | 123 | ```objc 124 | #define TEMPLATE_URL @"mgj://search/:keyword" 125 | 126 | [MGJRouter registerURLPattern:TEMPLATE_URL toHandler:^(NSDictionary *routerParameters) { 127 | NSLog(@"routerParameters[keyword]:%@", routerParameters[@"keyword"]); // Hangzhou 128 | }]; 129 | 130 | [MGJRouter openURL:[MGJRouter generateURLWithPattern:TEMPLATE_URL parameters:@[@"Hangzhou"]]]; 131 | } 132 | ``` 133 | 134 | 这样就可以在一个地方定义所有的 URL Pattern,使用时,用这个方法生成 URL 就行了。 135 | 136 | 137 | ## 协议 138 | 139 | MGJRouter 被许可在 MIT 协议下使用。查阅 LICENSE 文件来获得更多信息。 140 | -------------------------------------------------------------------------------- /MGJRouter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint MGJRequestManager.podspec.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "MGJRouter" 19 | s.version = "0.9.2" 20 | s.summary = "an smart iOS URL Router" 21 | 22 | s.homepage = "http://github.com/mogujie/MGJRouter" 23 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 24 | 25 | 26 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 27 | # 28 | # Licensing your code is important. See http://choosealicense.com for more info. 29 | # CocoaPods will detect a license file if there is a named LICENSE* 30 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 31 | # 32 | 33 | s.license = "MIT" 34 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 35 | 36 | 37 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 38 | # 39 | # Specify the authors of the library, with email addresses. Email addresses 40 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 41 | # accepts just a name if you'd rather not provide an email address. 42 | # 43 | # Specify a social_media_url where others can refer to, for example a twitter 44 | # profile URL. 45 | # 46 | 47 | s.author = { "lzyy" => "healdream@gmail.com" } 48 | 49 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 50 | # 51 | # If this Pod runs only on iOS or OS X, then specify the platform and 52 | # the deployment target. You can optionally include the target after the platform. 53 | # 54 | 55 | # s.platform = :ios 56 | s.platform = :ios, "6.0" 57 | 58 | # When using multiple platforms 59 | # s.ios.deployment_target = "5.0" 60 | # s.osx.deployment_target = "10.7" 61 | 62 | 63 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 64 | # 65 | # Specify the location from where the source should be retrieved. 66 | # Supports git, hg, bzr, svn and HTTP. 67 | # 68 | 69 | s.source = { :git => "https://github.com/mogujie/MGJRouter.git", :tag => s.version } 70 | 71 | 72 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 73 | # 74 | # CocoaPods is smart about how it includes source code. For source files 75 | # giving a folder will include any swift, h, m, mm, c & cpp files. 76 | # For header files it will include any header in the folder. 77 | # Not including the public_header_files will make all headers public. 78 | # 79 | 80 | s.source_files = "MGJRouter/*.{h,m}" 81 | 82 | # s.public_header_files = "Classes/**/*.h" 83 | 84 | 85 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 86 | # 87 | # A list of resources included with the Pod. These are copied into the 88 | # target bundle with a build phase script. Anything else will be cleaned. 89 | # You can preserve files from being cleaned, please don't preserve 90 | # non-essential files like tests, examples and documentation. 91 | # 92 | 93 | # s.resource = "icon.png" 94 | # s.resources = "Resources/*.png" 95 | 96 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 97 | 98 | 99 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 100 | # 101 | # Link your library with frameworks, or libraries. Libraries do not include 102 | # the lib prefix of their name. 103 | # 104 | 105 | # s.framework = "SomeFramework" 106 | # s.frameworks = "SomeFramework", "AnotherFramework" 107 | 108 | # s.library = "iconv" 109 | # s.libraries = "iconv", "xml2" 110 | 111 | 112 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 113 | # 114 | # If your library depends on compiler flags you can set them in the xcconfig hash 115 | # where they will only apply to your library. If you depend on other Podspecs 116 | # you can include multiple dependencies to ensure it works. 117 | 118 | # s.requires_arc = true 119 | 120 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 121 | 122 | end 123 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo/DemoDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoDetailViewController.m 3 | // MGJRequestManagerDemo 4 | // 5 | // Created by limboy on 3/20/15. 6 | // Copyright (c) 2015 juangua. All rights reserved. 7 | // 8 | 9 | #import "DemoDetailViewController.h" 10 | #import "DemoListViewController.h" 11 | #import "MGJRouter.h" 12 | 13 | @interface DemoDetailViewController () 14 | @property (nonatomic) UITextView *resultTextView; 15 | @property (nonatomic) SEL selectedSelector; 16 | @end 17 | 18 | @implementation DemoDetailViewController 19 | 20 | + (void)load 21 | { 22 | DemoDetailViewController *detailViewController = [[DemoDetailViewController alloc] init]; 23 | [DemoListViewController registerWithTitle:@"基本使用" handler:^UIViewController *{ 24 | detailViewController.selectedSelector = @selector(demoBasicUsage); 25 | return detailViewController; 26 | }]; 27 | 28 | [DemoListViewController registerWithTitle:@"中文匹配" handler:^UIViewController *{ 29 | detailViewController.selectedSelector = @selector(demoChineseCharacter); 30 | return detailViewController; 31 | }]; 32 | 33 | [DemoListViewController registerWithTitle:@"自定义参数" handler:^UIViewController *{ 34 | detailViewController.selectedSelector = @selector(demoParameters); 35 | return detailViewController; 36 | }]; 37 | 38 | [DemoListViewController registerWithTitle:@"传入字典信息" handler:^UIViewController *{ 39 | detailViewController.selectedSelector = @selector(demoUserInfo); 40 | return detailViewController; 41 | }]; 42 | 43 | [DemoListViewController registerWithTitle:@"Fallback 到全局的 URL Pattern" handler:^UIViewController *{ 44 | detailViewController.selectedSelector = @selector(demoFallback); 45 | return detailViewController; 46 | }]; 47 | 48 | [DemoListViewController registerWithTitle:@"Open 结束后执行 Completion Block" handler:^UIViewController *{ 49 | detailViewController.selectedSelector = @selector(demoCompletion); 50 | return detailViewController; 51 | }]; 52 | 53 | [DemoListViewController registerWithTitle:@"基于 URL 模板生成 具体的 URL" handler:^UIViewController *{ 54 | detailViewController.selectedSelector = @selector(demoGenerateURL); 55 | return detailViewController; 56 | }]; 57 | 58 | [DemoListViewController registerWithTitle:@"取消注册 URL Pattern" handler:^UIViewController *{ 59 | detailViewController.selectedSelector = @selector(demoDeregisterURLPattern); 60 | return detailViewController; 61 | }]; 62 | 63 | [DemoListViewController registerWithTitle:@"同步获取 URL 对应的 Object" handler:^UIViewController *{ 64 | detailViewController.selectedSelector = @selector(demoObjectForURL); 65 | return detailViewController; 66 | }]; 67 | } 68 | 69 | - (void)viewDidLoad { 70 | [super viewDidLoad]; 71 | self.view.backgroundColor = [UIColor colorWithRed:239.f/255 green:239.f/255 blue:244.f/255 alpha:1]; 72 | [self.view addSubview:self.resultTextView]; 73 | // Do any additional setup after loading the view. 74 | } 75 | 76 | - (void)appendLog:(NSString *)log 77 | { 78 | NSString *currentLog = self.resultTextView.text; 79 | if (currentLog.length) { 80 | currentLog = [currentLog stringByAppendingString:[NSString stringWithFormat:@"\n----------\n%@", log]]; 81 | } else { 82 | currentLog = log; 83 | } 84 | self.resultTextView.text = currentLog; 85 | [self.resultTextView sizeThatFits:CGSizeMake(self.view.frame.size.width, CGFLOAT_MAX)]; 86 | } 87 | 88 | - (void)viewWillAppear:(BOOL)animated 89 | { 90 | [super viewWillAppear:animated]; 91 | [self.resultTextView.subviews enumerateObjectsUsingBlock:^(UIImageView *obj, NSUInteger idx, BOOL *stop) { 92 | if ([obj isKindOfClass:[UIImageView class]]) { 93 | // 这个是为了去除显示图片时,添加的 imageView 94 | [obj removeFromSuperview]; 95 | } 96 | }]; 97 | } 98 | 99 | - (void)viewDidAppear:(BOOL)animated 100 | { 101 | [super viewDidAppear:animated]; 102 | [self.resultTextView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil]; 103 | [self performSelector:self.selectedSelector withObject:nil afterDelay:0]; 104 | } 105 | 106 | - (void)viewDidDisappear:(BOOL)animated 107 | { 108 | [super viewDidDisappear:animated]; 109 | [self.resultTextView removeObserver:self forKeyPath:@"contentSize"]; 110 | self.resultTextView.text = @""; 111 | } 112 | 113 | - (UITextView *)resultTextView 114 | { 115 | if (!_resultTextView) { 116 | NSInteger padding = 20; 117 | NSInteger viewWith = self.view.frame.size.width; 118 | NSInteger viewHeight = self.view.frame.size.height - 64; 119 | _resultTextView = [[UITextView alloc] initWithFrame:CGRectMake(padding, padding + 64, viewWith - padding * 2, viewHeight - padding * 2)]; 120 | _resultTextView.layer.borderColor = [UIColor colorWithWhite:0.8 alpha:1].CGColor; 121 | _resultTextView.layer.borderWidth = 1; 122 | _resultTextView.editable = NO; 123 | _resultTextView.contentInset = UIEdgeInsetsMake(-64, 0, 0, 0); 124 | _resultTextView.font = [UIFont systemFontOfSize:14]; 125 | _resultTextView.textColor = [UIColor colorWithWhite:0.2 alpha:1]; 126 | _resultTextView.contentOffset = CGPointZero; 127 | } 128 | return _resultTextView; 129 | } 130 | 131 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 132 | { 133 | if ([keyPath isEqualToString:@"contentSize"]) { 134 | NSInteger contentHeight = self.resultTextView.contentSize.height; 135 | NSInteger textViewHeight = self.resultTextView.frame.size.height; 136 | [self.resultTextView setContentOffset:CGPointMake(0, MAX(contentHeight - textViewHeight, 0)) animated:YES]; 137 | } 138 | } 139 | 140 | #pragma mark - Demos 141 | 142 | - (void)demoBasicUsage 143 | { 144 | [MGJRouter registerURLPattern:@"mgj://foo/bar" toHandler:^(NSDictionary *routerParameters) { 145 | [self appendLog:@"匹配到了 url,以下是相关信息"]; 146 | [self appendLog:[NSString stringWithFormat:@"routerParameters:%@", routerParameters]]; 147 | }]; 148 | 149 | [MGJRouter openURL:@"mgj://foo/bar"]; 150 | } 151 | 152 | - (void)demoChineseCharacter 153 | { 154 | [MGJRouter registerURLPattern:@"mgj://category/家居" toHandler:^(NSDictionary *routerParameters) { 155 | [self appendLog:@"匹配到了 url,以下是相关信息"]; 156 | [self appendLog:[NSString stringWithFormat:@"routerParameters:%@", routerParameters]]; 157 | }]; 158 | 159 | [MGJRouter openURL:@"mgj://category/家居"]; 160 | } 161 | 162 | - (void)demoUserInfo 163 | { 164 | [MGJRouter registerURLPattern:@"mgj://category/travel" toHandler:^(NSDictionary *routerParameters) { 165 | [self appendLog:@"匹配到了 url,以下是相关信息"]; 166 | [self appendLog:[NSString stringWithFormat:@"routerParameters:%@", routerParameters]]; 167 | }]; 168 | 169 | [MGJRouter openURL:@"mgj://category/travel" withUserInfo:@{@"user_id": @1900} completion:nil]; 170 | } 171 | 172 | - (void)demoParameters 173 | { 174 | [MGJRouter registerURLPattern:@"mgj://search/:query" toHandler:^(NSDictionary *routerParameters) { 175 | [self appendLog:@"匹配到了 url,以下是相关信息"]; 176 | [self appendLog:[NSString stringWithFormat:@"routerParameters:%@", routerParameters]]; 177 | }]; 178 | 179 | [MGJRouter openURL:@"mgj://search/bicycle?color=red"]; 180 | } 181 | 182 | - (void)demoFallback 183 | { 184 | [MGJRouter registerURLPattern:@"mgj://" toHandler:^(NSDictionary *routerParameters) { 185 | [self appendLog:@"匹配到了 url,以下是相关信息"]; 186 | [self appendLog:[NSString stringWithFormat:@"routerParameters:%@", routerParameters]]; 187 | }]; 188 | 189 | [MGJRouter openURL:@"mgj://search/travel/china?has_travelled=0"]; 190 | } 191 | 192 | - (void)demoCompletion 193 | { 194 | [MGJRouter registerURLPattern:@"mgj://detail" toHandler:^(NSDictionary *routerParameters) { 195 | NSLog(@"匹配到了 url, 一会会执行 Completion Block"); 196 | 197 | // 模拟 push 一个 VC 198 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 199 | void (^completion)(id result) = routerParameters[MGJRouterParameterCompletion]; 200 | if (completion) { 201 | completion(nil); 202 | } 203 | }); 204 | }]; 205 | 206 | [MGJRouter openURL:@"mgj://detail" withUserInfo:nil completion:^(id result){ 207 | [self appendLog:@"Open 结束,我是 Completion Block"]; 208 | }]; 209 | } 210 | 211 | - (void)demoGenerateURL 212 | { 213 | #define TEMPLATE_URL @"mgj://search/:keyword" 214 | 215 | [MGJRouter registerURLPattern:TEMPLATE_URL toHandler:^(NSDictionary *routerParameters) { 216 | NSLog(@"routerParameters[keyword]:%@", routerParameters[@"keyword"]); // Hangzhou 217 | }]; 218 | 219 | [MGJRouter openURL:[MGJRouter generateURLWithPattern:TEMPLATE_URL parameters:@[@"Hangzhou"]]]; 220 | } 221 | 222 | - (void)demoDeregisterURLPattern 223 | { 224 | #define TEMPLATE_URL @"mgj://search/:keyword" 225 | 226 | [MGJRouter registerURLPattern:TEMPLATE_URL toHandler:^(NSDictionary *routerParameters) { 227 | NSAssert(NO, @"这里不会被触发"); 228 | NSLog(@"routerParameters[keyword]:%@", routerParameters[@"keyword"]); // Hangzhou 229 | }]; 230 | 231 | [MGJRouter deregisterURLPattern:TEMPLATE_URL]; 232 | 233 | [MGJRouter openURL:[MGJRouter generateURLWithPattern:TEMPLATE_URL parameters:@[@"Hangzhou"]]]; 234 | 235 | [self appendLog:@"如果没有运行到断点,就表示取消注册成功了"]; 236 | } 237 | 238 | - (void)demoObjectForURL 239 | { 240 | [MGJRouter registerURLPattern:@"mgj://search_top_bar" toObjectHandler:^id(NSDictionary *routerParameters) { 241 | UIView *searchTopBar = [[UIView alloc] init]; 242 | return searchTopBar; 243 | }]; 244 | 245 | UIView *searchTopBar = [MGJRouter objectForURL:@"mgj://search_top_bar"]; 246 | 247 | if ([searchTopBar isKindOfClass:[UIView class]]) { 248 | [self appendLog:@"同步获取 Object 成功"]; 249 | } else { 250 | [self appendLog:@"同步获取 Object 失败"]; 251 | } 252 | } 253 | 254 | @end 255 | -------------------------------------------------------------------------------- /MGJRouter/MGJRouter.m: -------------------------------------------------------------------------------- 1 | // 2 | // MGJRouter.m 3 | // MGJFoundation 4 | // 5 | // Created by limboy on 12/9/14. 6 | // Copyright (c) 2014 juangua. All rights reserved. 7 | // 8 | 9 | #import "MGJRouter.h" 10 | #import 11 | 12 | static NSString * const MGJ_ROUTER_WILDCARD_CHARACTER = @"~"; 13 | static NSString *specialCharacters = @"/?&."; 14 | 15 | NSString *const MGJRouterParameterURL = @"MGJRouterParameterURL"; 16 | NSString *const MGJRouterParameterCompletion = @"MGJRouterParameterCompletion"; 17 | NSString *const MGJRouterParameterUserInfo = @"MGJRouterParameterUserInfo"; 18 | 19 | 20 | @interface MGJRouter () 21 | /** 22 | * 保存了所有已注册的 URL 23 | * 结构类似 @{@"beauty": @{@":id": {@"_", [block copy]}}} 24 | */ 25 | @property (nonatomic) NSMutableDictionary *routes; 26 | @end 27 | 28 | @implementation MGJRouter 29 | 30 | + (instancetype)sharedInstance 31 | { 32 | static MGJRouter *instance = nil; 33 | static dispatch_once_t onceToken; 34 | dispatch_once(&onceToken, ^{ 35 | instance = [[self alloc] init]; 36 | }); 37 | return instance; 38 | } 39 | 40 | + (void)registerURLPattern:(NSString *)URLPattern toHandler:(MGJRouterHandler)handler 41 | { 42 | [[self sharedInstance] addURLPattern:URLPattern andHandler:handler]; 43 | } 44 | 45 | + (void)deregisterURLPattern:(NSString *)URLPattern 46 | { 47 | [[self sharedInstance] removeURLPattern:URLPattern]; 48 | } 49 | 50 | + (void)openURL:(NSString *)URL 51 | { 52 | [self openURL:URL completion:nil]; 53 | } 54 | 55 | + (void)openURL:(NSString *)URL completion:(void (^)(id result))completion 56 | { 57 | [self openURL:URL withUserInfo:nil completion:completion]; 58 | } 59 | 60 | + (void)openURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo completion:(void (^)(id result))completion 61 | { 62 | URL = [URL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 63 | NSMutableDictionary *parameters = [[self sharedInstance] extractParametersFromURL:URL]; 64 | 65 | [parameters enumerateKeysAndObjectsUsingBlock:^(id key, NSString *obj, BOOL *stop) { 66 | if ([obj isKindOfClass:[NSString class]]) { 67 | parameters[key] = [obj stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 68 | } 69 | }]; 70 | 71 | if (parameters) { 72 | MGJRouterHandler handler = parameters[@"block"]; 73 | if (completion) { 74 | parameters[MGJRouterParameterCompletion] = completion; 75 | } 76 | if (userInfo) { 77 | parameters[MGJRouterParameterUserInfo] = userInfo; 78 | } 79 | if (handler) { 80 | [parameters removeObjectForKey:@"block"]; 81 | handler(parameters); 82 | } 83 | } 84 | } 85 | 86 | + (BOOL)canOpenURL:(NSString *)URL 87 | { 88 | return [[self sharedInstance] extractParametersFromURL:URL] ? YES : NO; 89 | } 90 | 91 | + (NSString *)generateURLWithPattern:(NSString *)pattern parameters:(NSArray *)parameters 92 | { 93 | NSInteger startIndexOfColon = 0; 94 | 95 | NSMutableArray *placeholders = [NSMutableArray array]; 96 | 97 | for (int i = 0; i < pattern.length; i++) { 98 | NSString *character = [NSString stringWithFormat:@"%c", [pattern characterAtIndex:i]]; 99 | if ([character isEqualToString:@":"]) { 100 | startIndexOfColon = i; 101 | } 102 | if ([specialCharacters rangeOfString:character].location != NSNotFound && i > (startIndexOfColon + 1) && startIndexOfColon) { 103 | NSRange range = NSMakeRange(startIndexOfColon, i - startIndexOfColon); 104 | NSString *placeholder = [pattern substringWithRange:range]; 105 | if (![self checkIfContainsSpecialCharacter:placeholder]) { 106 | [placeholders addObject:placeholder]; 107 | startIndexOfColon = 0; 108 | } 109 | } 110 | if (i == pattern.length - 1 && startIndexOfColon) { 111 | NSRange range = NSMakeRange(startIndexOfColon, i - startIndexOfColon + 1); 112 | NSString *placeholder = [pattern substringWithRange:range]; 113 | if (![self checkIfContainsSpecialCharacter:placeholder]) { 114 | [placeholders addObject:placeholder]; 115 | } 116 | } 117 | } 118 | 119 | __block NSString *parsedResult = pattern; 120 | 121 | [placeholders enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) { 122 | idx = parameters.count > idx ? idx : parameters.count - 1; 123 | parsedResult = [parsedResult stringByReplacingOccurrencesOfString:obj withString:parameters[idx]]; 124 | }]; 125 | 126 | return parsedResult; 127 | } 128 | 129 | + (id)objectForURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo 130 | { 131 | MGJRouter *router = [MGJRouter sharedInstance]; 132 | 133 | URL = [URL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 134 | NSMutableDictionary *parameters = [router extractParametersFromURL:URL]; 135 | MGJRouterObjectHandler handler = parameters[@"block"]; 136 | 137 | if (handler) { 138 | if (userInfo) { 139 | parameters[MGJRouterParameterUserInfo] = userInfo; 140 | } 141 | [parameters removeObjectForKey:@"block"]; 142 | return handler(parameters); 143 | } 144 | return nil; 145 | } 146 | 147 | + (id)objectForURL:(NSString *)URL 148 | { 149 | return [self objectForURL:URL withUserInfo:nil]; 150 | } 151 | 152 | + (void)registerURLPattern:(NSString *)URLPattern toObjectHandler:(MGJRouterObjectHandler)handler 153 | { 154 | [[self sharedInstance] addURLPattern:URLPattern andObjectHandler:handler]; 155 | } 156 | 157 | - (void)addURLPattern:(NSString *)URLPattern andHandler:(MGJRouterHandler)handler 158 | { 159 | NSMutableDictionary *subRoutes = [self addURLPattern:URLPattern]; 160 | if (handler && subRoutes) { 161 | subRoutes[@"_"] = [handler copy]; 162 | } 163 | } 164 | 165 | - (void)addURLPattern:(NSString *)URLPattern andObjectHandler:(MGJRouterObjectHandler)handler 166 | { 167 | NSMutableDictionary *subRoutes = [self addURLPattern:URLPattern]; 168 | if (handler && subRoutes) { 169 | subRoutes[@"_"] = [handler copy]; 170 | } 171 | } 172 | 173 | - (NSMutableDictionary *)addURLPattern:(NSString *)URLPattern 174 | { 175 | NSArray *pathComponents = [self pathComponentsFromURL:URLPattern]; 176 | 177 | NSInteger index = 0; 178 | NSMutableDictionary* subRoutes = self.routes; 179 | 180 | while (index < pathComponents.count) { 181 | NSString* pathComponent = pathComponents[index]; 182 | if (![subRoutes objectForKey:pathComponent]) { 183 | subRoutes[pathComponent] = [[NSMutableDictionary alloc] init]; 184 | } 185 | subRoutes = subRoutes[pathComponent]; 186 | index++; 187 | } 188 | return subRoutes; 189 | } 190 | 191 | #pragma mark - Utils 192 | 193 | - (NSMutableDictionary *)extractParametersFromURL:(NSString *)url 194 | { 195 | NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; 196 | 197 | parameters[MGJRouterParameterURL] = url; 198 | 199 | NSMutableDictionary* subRoutes = self.routes; 200 | NSArray* pathComponents = [self pathComponentsFromURL:url]; 201 | 202 | // borrowed from HHRouter(https://github.com/Huohua/HHRouter) 203 | for (NSString* pathComponent in pathComponents) { 204 | BOOL found = NO; 205 | 206 | // 对 key 进行排序,这样可以把 ~ 放到最后 207 | NSArray *subRoutesKeys =[subRoutes.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) { 208 | return [obj1 compare:obj2]; 209 | }]; 210 | 211 | for (NSString* key in subRoutesKeys) { 212 | if ([key isEqualToString:pathComponent] || [key isEqualToString:MGJ_ROUTER_WILDCARD_CHARACTER]) { 213 | found = YES; 214 | subRoutes = subRoutes[key]; 215 | break; 216 | } else if ([key hasPrefix:@":"]) { 217 | found = YES; 218 | subRoutes = subRoutes[key]; 219 | NSString *newKey = [key substringFromIndex:1]; 220 | NSString *newPathComponent = pathComponent; 221 | // 再做一下特殊处理,比如 :id.html -> :id 222 | if ([self.class checkIfContainsSpecialCharacter:key]) { 223 | NSCharacterSet *specialCharacterSet = [NSCharacterSet characterSetWithCharactersInString:specialCharacters]; 224 | NSRange range = [key rangeOfCharacterFromSet:specialCharacterSet]; 225 | if (range.location != NSNotFound) { 226 | // 把 pathComponent 后面的部分也去掉 227 | newKey = [newKey substringToIndex:range.location - 1]; 228 | NSString *suffixToStrip = [key substringFromIndex:range.location]; 229 | newPathComponent = [newPathComponent stringByReplacingOccurrencesOfString:suffixToStrip withString:@""]; 230 | } 231 | } 232 | parameters[newKey] = newPathComponent; 233 | break; 234 | } 235 | } 236 | // 如果没有找到该 pathComponent 对应的 handler,则以上一层的 handler 作为 fallback 237 | if (!found && !subRoutes[@"_"]) { 238 | return nil; 239 | } 240 | } 241 | 242 | // Extract Params From Query. 243 | NSArray* pathInfo = [url componentsSeparatedByString:@"?"]; 244 | if (pathInfo.count > 1) { 245 | NSString* parametersString = [pathInfo objectAtIndex:1]; 246 | NSArray* paramStringArr = [parametersString componentsSeparatedByString:@"&"]; 247 | for (NSString* paramString in paramStringArr) { 248 | NSArray* paramArr = [paramString componentsSeparatedByString:@"="]; 249 | if (paramArr.count > 1) { 250 | NSString* key = [paramArr objectAtIndex:0]; 251 | NSString* value = [paramArr objectAtIndex:1]; 252 | parameters[key] = value; 253 | } 254 | } 255 | } 256 | 257 | if (subRoutes[@"_"]) { 258 | parameters[@"block"] = [subRoutes[@"_"] copy]; 259 | } 260 | 261 | return parameters; 262 | } 263 | 264 | - (void)removeURLPattern:(NSString *)URLPattern 265 | { 266 | NSMutableArray *pathComponents = [NSMutableArray arrayWithArray:[self pathComponentsFromURL:URLPattern]]; 267 | 268 | // 只删除该 pattern 的最后一级 269 | if (pathComponents.count >= 1) { 270 | // 假如 URLPattern 为 a/b/c, components 就是 @"a.b.c" 正好可以作为 KVC 的 key 271 | NSString *components = [pathComponents componentsJoinedByString:@"."]; 272 | NSMutableDictionary *route = [self.routes valueForKeyPath:components]; 273 | 274 | if (route.count >= 1) { 275 | NSString *lastComponent = [pathComponents lastObject]; 276 | [pathComponents removeLastObject]; 277 | 278 | // 有可能是根 key,这样就是 self.routes 了 279 | route = self.routes; 280 | if (pathComponents.count) { 281 | NSString *componentsWithoutLast = [pathComponents componentsJoinedByString:@"."]; 282 | route = [self.routes valueForKeyPath:componentsWithoutLast]; 283 | } 284 | [route removeObjectForKey:lastComponent]; 285 | } 286 | } 287 | } 288 | 289 | - (NSArray*)pathComponentsFromURL:(NSString*)URL 290 | { 291 | NSMutableArray *pathComponents = [NSMutableArray array]; 292 | if ([URL rangeOfString:@"://"].location != NSNotFound) { 293 | NSArray *pathSegments = [URL componentsSeparatedByString:@"://"]; 294 | // 如果 URL 包含协议,那么把协议作为第一个元素放进去 295 | [pathComponents addObject:pathSegments[0]]; 296 | 297 | // 如果只有协议,那么放一个占位符 298 | if ((pathSegments.count == 2 && ((NSString *)pathSegments[1]).length) || pathSegments.count < 2) { 299 | [pathComponents addObject:MGJ_ROUTER_WILDCARD_CHARACTER]; 300 | } 301 | 302 | URL = [URL substringFromIndex:[URL rangeOfString:@"://"].location + 3]; 303 | } 304 | 305 | for (NSString *pathComponent in [[NSURL URLWithString:URL] pathComponents]) { 306 | if ([pathComponent isEqualToString:@"/"]) continue; 307 | if ([[pathComponent substringToIndex:1] isEqualToString:@"?"]) break; 308 | [pathComponents addObject:pathComponent]; 309 | } 310 | return [pathComponents copy]; 311 | } 312 | 313 | - (NSMutableDictionary *)routes 314 | { 315 | if (!_routes) { 316 | _routes = [[NSMutableDictionary alloc] init]; 317 | } 318 | return _routes; 319 | } 320 | 321 | #pragma mark - Utils 322 | 323 | + (BOOL)checkIfContainsSpecialCharacter:(NSString *)checkedString { 324 | NSCharacterSet *specialCharactersSet = [NSCharacterSet characterSetWithCharactersInString:specialCharacters]; 325 | return [checkedString rangeOfCharacterFromSet:specialCharactersSet].location != NSNotFound; 326 | } 327 | 328 | @end 329 | -------------------------------------------------------------------------------- /MGJRouterDemo/MGJRouterDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6AA6276C1B1574CA003A6B2B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AA6276B1B1574CA003A6B2B /* main.m */; }; 11 | 6AA6276F1B1574CA003A6B2B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AA6276E1B1574CA003A6B2B /* AppDelegate.m */; }; 12 | 6AA627771B1574CA003A6B2B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6AA627761B1574CA003A6B2B /* Images.xcassets */; }; 13 | 6AA6277A1B1574CA003A6B2B /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6AA627781B1574CA003A6B2B /* LaunchScreen.xib */; }; 14 | 6AA627861B1574CA003A6B2B /* MGJRouterDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AA627851B1574CA003A6B2B /* MGJRouterDemoTests.m */; }; 15 | 6AA627921B157940003A6B2B /* MGJRouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AA627911B157940003A6B2B /* MGJRouter.m */; }; 16 | 6AA6279F1B15AFC9003A6B2B /* DemoListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AA6279E1B15AFC9003A6B2B /* DemoListViewController.m */; }; 17 | 6AA627A21B15AFFE003A6B2B /* DemoDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6AA627A11B15AFFE003A6B2B /* DemoDetailViewController.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 6AA627801B1574CA003A6B2B /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 6AA6275E1B1574CA003A6B2B /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 6AA627651B1574CA003A6B2B; 26 | remoteInfo = MGJRouterDemo; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 6AA627661B1574CA003A6B2B /* MGJRouterDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MGJRouterDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 6AA6276A1B1574CA003A6B2B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 6AA6276B1B1574CA003A6B2B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 34 | 6AA6276D1B1574CA003A6B2B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 35 | 6AA6276E1B1574CA003A6B2B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 36 | 6AA627761B1574CA003A6B2B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 37 | 6AA627791B1574CA003A6B2B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 38 | 6AA6277F1B1574CA003A6B2B /* MGJRouterDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MGJRouterDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 6AA627841B1574CA003A6B2B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 6AA627851B1574CA003A6B2B /* MGJRouterDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGJRouterDemoTests.m; sourceTree = ""; }; 41 | 6AA627901B157940003A6B2B /* MGJRouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGJRouter.h; sourceTree = ""; }; 42 | 6AA627911B157940003A6B2B /* MGJRouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGJRouter.m; sourceTree = ""; }; 43 | 6AA6279D1B15AFC9003A6B2B /* DemoListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoListViewController.h; sourceTree = ""; }; 44 | 6AA6279E1B15AFC9003A6B2B /* DemoListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoListViewController.m; sourceTree = ""; }; 45 | 6AA627A01B15AFFE003A6B2B /* DemoDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoDetailViewController.h; sourceTree = ""; }; 46 | 6AA627A11B15AFFE003A6B2B /* DemoDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoDetailViewController.m; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 6AA627631B1574CA003A6B2B /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | 6AA6277C1B1574CA003A6B2B /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 6AA6275D1B1574CA003A6B2B = { 68 | isa = PBXGroup; 69 | children = ( 70 | 6AA6278F1B157940003A6B2B /* MGJRouter */, 71 | 6AA627681B1574CA003A6B2B /* MGJRouterDemo */, 72 | 6AA627821B1574CA003A6B2B /* MGJRouterDemoTests */, 73 | 6AA627671B1574CA003A6B2B /* Products */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | 6AA627671B1574CA003A6B2B /* Products */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 6AA627661B1574CA003A6B2B /* MGJRouterDemo.app */, 81 | 6AA6277F1B1574CA003A6B2B /* MGJRouterDemoTests.xctest */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 6AA627681B1574CA003A6B2B /* MGJRouterDemo */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 6AA6276D1B1574CA003A6B2B /* AppDelegate.h */, 90 | 6AA6276E1B1574CA003A6B2B /* AppDelegate.m */, 91 | 6AA6279D1B15AFC9003A6B2B /* DemoListViewController.h */, 92 | 6AA6279E1B15AFC9003A6B2B /* DemoListViewController.m */, 93 | 6AA627A01B15AFFE003A6B2B /* DemoDetailViewController.h */, 94 | 6AA627A11B15AFFE003A6B2B /* DemoDetailViewController.m */, 95 | 6AA627761B1574CA003A6B2B /* Images.xcassets */, 96 | 6AA627781B1574CA003A6B2B /* LaunchScreen.xib */, 97 | 6AA627691B1574CA003A6B2B /* Supporting Files */, 98 | ); 99 | path = MGJRouterDemo; 100 | sourceTree = ""; 101 | }; 102 | 6AA627691B1574CA003A6B2B /* Supporting Files */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 6AA6276A1B1574CA003A6B2B /* Info.plist */, 106 | 6AA6276B1B1574CA003A6B2B /* main.m */, 107 | ); 108 | name = "Supporting Files"; 109 | sourceTree = ""; 110 | }; 111 | 6AA627821B1574CA003A6B2B /* MGJRouterDemoTests */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 6AA627851B1574CA003A6B2B /* MGJRouterDemoTests.m */, 115 | 6AA627831B1574CA003A6B2B /* Supporting Files */, 116 | ); 117 | path = MGJRouterDemoTests; 118 | sourceTree = ""; 119 | }; 120 | 6AA627831B1574CA003A6B2B /* Supporting Files */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 6AA627841B1574CA003A6B2B /* Info.plist */, 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | 6AA6278F1B157940003A6B2B /* MGJRouter */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 6AA627901B157940003A6B2B /* MGJRouter.h */, 132 | 6AA627911B157940003A6B2B /* MGJRouter.m */, 133 | ); 134 | name = MGJRouter; 135 | path = ../MGJRouter; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 6AA627651B1574CA003A6B2B /* MGJRouterDemo */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 6AA627891B1574CA003A6B2B /* Build configuration list for PBXNativeTarget "MGJRouterDemo" */; 144 | buildPhases = ( 145 | 6AA627621B1574CA003A6B2B /* Sources */, 146 | 6AA627631B1574CA003A6B2B /* Frameworks */, 147 | 6AA627641B1574CA003A6B2B /* Resources */, 148 | ); 149 | buildRules = ( 150 | ); 151 | dependencies = ( 152 | ); 153 | name = MGJRouterDemo; 154 | productName = MGJRouterDemo; 155 | productReference = 6AA627661B1574CA003A6B2B /* MGJRouterDemo.app */; 156 | productType = "com.apple.product-type.application"; 157 | }; 158 | 6AA6277E1B1574CA003A6B2B /* MGJRouterDemoTests */ = { 159 | isa = PBXNativeTarget; 160 | buildConfigurationList = 6AA6278C1B1574CA003A6B2B /* Build configuration list for PBXNativeTarget "MGJRouterDemoTests" */; 161 | buildPhases = ( 162 | 6AA6277B1B1574CA003A6B2B /* Sources */, 163 | 6AA6277C1B1574CA003A6B2B /* Frameworks */, 164 | 6AA6277D1B1574CA003A6B2B /* Resources */, 165 | ); 166 | buildRules = ( 167 | ); 168 | dependencies = ( 169 | 6AA627811B1574CA003A6B2B /* PBXTargetDependency */, 170 | ); 171 | name = MGJRouterDemoTests; 172 | productName = MGJRouterDemoTests; 173 | productReference = 6AA6277F1B1574CA003A6B2B /* MGJRouterDemoTests.xctest */; 174 | productType = "com.apple.product-type.bundle.unit-test"; 175 | }; 176 | /* End PBXNativeTarget section */ 177 | 178 | /* Begin PBXProject section */ 179 | 6AA6275E1B1574CA003A6B2B /* Project object */ = { 180 | isa = PBXProject; 181 | attributes = { 182 | LastUpgradeCheck = 0630; 183 | ORGANIZATIONNAME = juangua; 184 | TargetAttributes = { 185 | 6AA627651B1574CA003A6B2B = { 186 | CreatedOnToolsVersion = 6.3.1; 187 | }; 188 | 6AA6277E1B1574CA003A6B2B = { 189 | CreatedOnToolsVersion = 6.3.1; 190 | TestTargetID = 6AA627651B1574CA003A6B2B; 191 | }; 192 | }; 193 | }; 194 | buildConfigurationList = 6AA627611B1574CA003A6B2B /* Build configuration list for PBXProject "MGJRouterDemo" */; 195 | compatibilityVersion = "Xcode 3.2"; 196 | developmentRegion = English; 197 | hasScannedForEncodings = 0; 198 | knownRegions = ( 199 | en, 200 | Base, 201 | ); 202 | mainGroup = 6AA6275D1B1574CA003A6B2B; 203 | productRefGroup = 6AA627671B1574CA003A6B2B /* Products */; 204 | projectDirPath = ""; 205 | projectRoot = ""; 206 | targets = ( 207 | 6AA627651B1574CA003A6B2B /* MGJRouterDemo */, 208 | 6AA6277E1B1574CA003A6B2B /* MGJRouterDemoTests */, 209 | ); 210 | }; 211 | /* End PBXProject section */ 212 | 213 | /* Begin PBXResourcesBuildPhase section */ 214 | 6AA627641B1574CA003A6B2B /* Resources */ = { 215 | isa = PBXResourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 6AA6277A1B1574CA003A6B2B /* LaunchScreen.xib in Resources */, 219 | 6AA627771B1574CA003A6B2B /* Images.xcassets in Resources */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | 6AA6277D1B1574CA003A6B2B /* Resources */ = { 224 | isa = PBXResourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | /* End PBXResourcesBuildPhase section */ 231 | 232 | /* Begin PBXSourcesBuildPhase section */ 233 | 6AA627621B1574CA003A6B2B /* Sources */ = { 234 | isa = PBXSourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | 6AA627921B157940003A6B2B /* MGJRouter.m in Sources */, 238 | 6AA6276F1B1574CA003A6B2B /* AppDelegate.m in Sources */, 239 | 6AA6276C1B1574CA003A6B2B /* main.m in Sources */, 240 | 6AA627A21B15AFFE003A6B2B /* DemoDetailViewController.m in Sources */, 241 | 6AA6279F1B15AFC9003A6B2B /* DemoListViewController.m in Sources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | 6AA6277B1B1574CA003A6B2B /* Sources */ = { 246 | isa = PBXSourcesBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | 6AA627861B1574CA003A6B2B /* MGJRouterDemoTests.m in Sources */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | /* End PBXSourcesBuildPhase section */ 254 | 255 | /* Begin PBXTargetDependency section */ 256 | 6AA627811B1574CA003A6B2B /* PBXTargetDependency */ = { 257 | isa = PBXTargetDependency; 258 | target = 6AA627651B1574CA003A6B2B /* MGJRouterDemo */; 259 | targetProxy = 6AA627801B1574CA003A6B2B /* PBXContainerItemProxy */; 260 | }; 261 | /* End PBXTargetDependency section */ 262 | 263 | /* Begin PBXVariantGroup section */ 264 | 6AA627781B1574CA003A6B2B /* LaunchScreen.xib */ = { 265 | isa = PBXVariantGroup; 266 | children = ( 267 | 6AA627791B1574CA003A6B2B /* Base */, 268 | ); 269 | name = LaunchScreen.xib; 270 | sourceTree = ""; 271 | }; 272 | /* End PBXVariantGroup section */ 273 | 274 | /* Begin XCBuildConfiguration section */ 275 | 6AA627871B1574CA003A6B2B /* Debug */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | ALWAYS_SEARCH_USER_PATHS = NO; 279 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 280 | CLANG_CXX_LIBRARY = "libc++"; 281 | CLANG_ENABLE_MODULES = YES; 282 | CLANG_ENABLE_OBJC_ARC = YES; 283 | CLANG_WARN_BOOL_CONVERSION = YES; 284 | CLANG_WARN_CONSTANT_CONVERSION = YES; 285 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 286 | CLANG_WARN_EMPTY_BODY = YES; 287 | CLANG_WARN_ENUM_CONVERSION = YES; 288 | CLANG_WARN_INT_CONVERSION = YES; 289 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 290 | CLANG_WARN_UNREACHABLE_CODE = YES; 291 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 292 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 293 | COPY_PHASE_STRIP = NO; 294 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 295 | ENABLE_STRICT_OBJC_MSGSEND = YES; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_DYNAMIC_NO_PIC = NO; 298 | GCC_NO_COMMON_BLOCKS = YES; 299 | GCC_OPTIMIZATION_LEVEL = 0; 300 | GCC_PREPROCESSOR_DEFINITIONS = ( 301 | "DEBUG=1", 302 | "$(inherited)", 303 | ); 304 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 305 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 306 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 307 | GCC_WARN_UNDECLARED_SELECTOR = YES; 308 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 309 | GCC_WARN_UNUSED_FUNCTION = YES; 310 | GCC_WARN_UNUSED_VARIABLE = YES; 311 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 312 | MTL_ENABLE_DEBUG_INFO = YES; 313 | ONLY_ACTIVE_ARCH = YES; 314 | SDKROOT = iphoneos; 315 | }; 316 | name = Debug; 317 | }; 318 | 6AA627881B1574CA003A6B2B /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ALWAYS_SEARCH_USER_PATHS = NO; 322 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 323 | CLANG_CXX_LIBRARY = "libc++"; 324 | CLANG_ENABLE_MODULES = YES; 325 | CLANG_ENABLE_OBJC_ARC = YES; 326 | CLANG_WARN_BOOL_CONVERSION = YES; 327 | CLANG_WARN_CONSTANT_CONVERSION = YES; 328 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 329 | CLANG_WARN_EMPTY_BODY = YES; 330 | CLANG_WARN_ENUM_CONVERSION = YES; 331 | CLANG_WARN_INT_CONVERSION = YES; 332 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 333 | CLANG_WARN_UNREACHABLE_CODE = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 336 | COPY_PHASE_STRIP = NO; 337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 338 | ENABLE_NS_ASSERTIONS = NO; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | GCC_C_LANGUAGE_STANDARD = gnu99; 341 | GCC_NO_COMMON_BLOCKS = YES; 342 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 343 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 344 | GCC_WARN_UNDECLARED_SELECTOR = YES; 345 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 346 | GCC_WARN_UNUSED_FUNCTION = YES; 347 | GCC_WARN_UNUSED_VARIABLE = YES; 348 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 349 | MTL_ENABLE_DEBUG_INFO = NO; 350 | SDKROOT = iphoneos; 351 | VALIDATE_PRODUCT = YES; 352 | }; 353 | name = Release; 354 | }; 355 | 6AA6278A1B1574CA003A6B2B /* Debug */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 359 | INFOPLIST_FILE = MGJRouterDemo/Info.plist; 360 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 361 | PRODUCT_NAME = "$(TARGET_NAME)"; 362 | }; 363 | name = Debug; 364 | }; 365 | 6AA6278B1B1574CA003A6B2B /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 369 | INFOPLIST_FILE = MGJRouterDemo/Info.plist; 370 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | }; 373 | name = Release; 374 | }; 375 | 6AA6278D1B1574CA003A6B2B /* Debug */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | BUNDLE_LOADER = "$(TEST_HOST)"; 379 | FRAMEWORK_SEARCH_PATHS = ( 380 | "$(SDKROOT)/Developer/Library/Frameworks", 381 | "$(inherited)", 382 | ); 383 | GCC_PREPROCESSOR_DEFINITIONS = ( 384 | "DEBUG=1", 385 | "$(inherited)", 386 | ); 387 | INFOPLIST_FILE = MGJRouterDemoTests/Info.plist; 388 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 389 | PRODUCT_NAME = "$(TARGET_NAME)"; 390 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MGJRouterDemo.app/MGJRouterDemo"; 391 | }; 392 | name = Debug; 393 | }; 394 | 6AA6278E1B1574CA003A6B2B /* Release */ = { 395 | isa = XCBuildConfiguration; 396 | buildSettings = { 397 | BUNDLE_LOADER = "$(TEST_HOST)"; 398 | FRAMEWORK_SEARCH_PATHS = ( 399 | "$(SDKROOT)/Developer/Library/Frameworks", 400 | "$(inherited)", 401 | ); 402 | INFOPLIST_FILE = MGJRouterDemoTests/Info.plist; 403 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 404 | PRODUCT_NAME = "$(TARGET_NAME)"; 405 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MGJRouterDemo.app/MGJRouterDemo"; 406 | }; 407 | name = Release; 408 | }; 409 | /* End XCBuildConfiguration section */ 410 | 411 | /* Begin XCConfigurationList section */ 412 | 6AA627611B1574CA003A6B2B /* Build configuration list for PBXProject "MGJRouterDemo" */ = { 413 | isa = XCConfigurationList; 414 | buildConfigurations = ( 415 | 6AA627871B1574CA003A6B2B /* Debug */, 416 | 6AA627881B1574CA003A6B2B /* Release */, 417 | ); 418 | defaultConfigurationIsVisible = 0; 419 | defaultConfigurationName = Release; 420 | }; 421 | 6AA627891B1574CA003A6B2B /* Build configuration list for PBXNativeTarget "MGJRouterDemo" */ = { 422 | isa = XCConfigurationList; 423 | buildConfigurations = ( 424 | 6AA6278A1B1574CA003A6B2B /* Debug */, 425 | 6AA6278B1B1574CA003A6B2B /* Release */, 426 | ); 427 | defaultConfigurationIsVisible = 0; 428 | }; 429 | 6AA6278C1B1574CA003A6B2B /* Build configuration list for PBXNativeTarget "MGJRouterDemoTests" */ = { 430 | isa = XCConfigurationList; 431 | buildConfigurations = ( 432 | 6AA6278D1B1574CA003A6B2B /* Debug */, 433 | 6AA6278E1B1574CA003A6B2B /* Release */, 434 | ); 435 | defaultConfigurationIsVisible = 0; 436 | }; 437 | /* End XCConfigurationList section */ 438 | }; 439 | rootObject = 6AA6275E1B1574CA003A6B2B /* Project object */; 440 | } 441 | --------------------------------------------------------------------------------