接上一篇移動端路由層設計
為啥要說iOS路由呢?
路由層其實在邏輯上的設計都是一樣的,關於對介面跳轉的實現部分卻與Android平臺和iOS平臺上的導航機制有著非常緊密的關係,Android作業系統有著天然的架構優勢,Intent機制可以協助應用間的互動與通訊,是對呼叫元件和資料傳遞的描述,本身這種機制就解除了程式碼邏輯和介面之間的依賴關係,只有資料依賴。而iOS的介面導航和轉場機制則大部分依賴UI元件各自的實現,所以如何解決這個問題,iOS端路由的實現則比較有代表性。
其實說白一點,路由層解決的核心問題就是原來介面或者元件之間相互呼叫都必須相互依賴,需要匯入目標的標頭檔案、需要清楚目標物件的邏輯,而現在全部都通過路由中轉,只依賴路由,或者依靠一些訊息傳遞機制連路由都不依賴。其次,路由的核心邏輯就是目標匹配,對於外部呼叫的情況來說,URL如何匹配Handler是最為重要的,匹配就必然用到正規表示式。瞭解這些關鍵點以後就有了設計的目的性,let‘s do it~
設計類圖:
這裡面有如下幾個類:
- WLRRouteRequest,路由層的請求,無論是跨應用的外部呼叫還是內部呼叫,最後都形成一個路由請求,該請求包含了URL上的queryparameters和路徑引數,還有內部呼叫時直接傳入的原生引數,還有請求發起者對目標預留的回撥block
- WLRRouteHandler,路由層的handler處理,handler接收一個WLRRouteRequest物件,來完成是否是介面跳轉,還是元件載入,還是內部邏輯
- WLRRouter,路由核心物件,內部持有註冊的Handler,比方說負責介面跳轉的Handler,負責元件載入的Handler,負責API的Handler等等,路由的作用就是將外部呼叫傳入的URL或者是內部呼叫傳入的target,在內部匹配上對應的handler,然後呼叫生命週期方法,完成處理過程,當然,圖中還有route的中介軟體,實際上是預留AOP的口子,方面後期擴充套件
- WLRRouteMatcher,用以處理外部呼叫的URL是否能與預設的正規表示式匹配,在WLRRouter中,每一次註冊一個handler,都會用一個URL匹配的表示式生成一個WLRRouteMatcher
- WLRRegularExpression,繼承NSRegularExpression,用以匹配URL,WLRRouteMatcher內部有一個WLRRegularExpression物件,WLRRouteMatcher接受一個URL,會使用WLRRegularExpression生成一個WLRMatchResult物件,來確定是否匹配成功,如果匹配成果則將URL上的路徑引數給取出來
- WLRMatchResult,用以描述WLRRegularExpression的匹配結果,包含路徑引數
工作流程:
- App啟動例項化WLRRouter物件
- 例項化WLRRouteHandler物件
- WLRRouter物件掛載WLRRouteHandler例項與URL的表示式相對應,WLRRouter內部生成一個WLRRouteMatcher物件,與URL的表示式相對應
- 外部呼叫的URL和callback傳入WLRRouter物件
- WLRRouter物件遍歷內部持有的URL的匹配表示式,並找到每一個WLRRouteMatcher物件,將URL傳入看是否能返回WLRRouteRequest物件
- 將WLRRouteRequest物件傳入對應的WLRRouteHandler物件
- WLRRouteHandler物件根據WLRRouteRequest尋找到TargetViewController和SourceViewController,在生命週期函式裡,完成引數傳遞與檢視轉場
WLRRouteRequest:
瞭解了以上,我們從WLRRouteRequest入手。
其實WLRRouteRequest跟NSURLRequest差不多,不過WLRRouteRequest繼承NSObject,實現NSCopying協議,大概如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#import @interface WLRRouteRequest : NSObject //外部呼叫的URL @property (nonatomic, copy, readonly) NSURL *URL; //URL表示式,比方說呼叫登入介面的表示式可以為:AppScheme://user/login/138********,那URL的匹配表示式可以是:/login/:phone([0-9]+),路徑必須以/login開頭,後面接0-9的電話號碼數字,當然你也可以直接把電話號碼的正則匹配寫全 @property(nonatomic,copy)NSString * routeExpression; //如果URL是AppScheme://user/login/138********?/callBack="",那麼這個callBack就出現在這 @property (nonatomic, copy, readonly) NSDictionary *queryParameters; //這裡面會出現{@"phone":@"138********"} @property (nonatomic, copy, readonly) NSDictionary *routeParameters; //這裡面存放的是內部呼叫傳遞的原生引數 @property (nonatomic, copy, readonly) NSDictionary *primitiveParams; //自動檢測竊取回撥的callBack 的Url @property (nonatomic, strong) NSURL *callbackURL; //目標的viewcontrolller或者是元件可以通過這個 @property(nonatomic,copy)void(^targetCallBack)(NSError *error,id responseObject); //用以表明該request是否被消費 @property(nonatomic)BOOL isConsumed; //簡便方法,用以下標法取引數 - (id)objectForKeyedSubscript:(NSString *)key; //初始化方法 -(instancetype)initWithURL:(NSURL *)URL routeExpression:(NSString *)routeExpression routeParameters:(NSDictionary *)routeParameters primitiveParameters:(NSDictionary *)primitiveParameters targetCallBack:(void(^)(NSError * error,id responseObject))targetCallBack; -(instancetype)initWithURL:(NSURL *)URL; //預設完成目標的回撥 -(void)defaultFinishTargetCallBack; @end |
NSURLRequest其實應該是個值型別的物件,所以實現拷貝協議,該物件的實現部分沒有什麼可講的,對照原始碼查閱即可。
WLRRouteHandler
1 2 3 4 5 6 7 8 9 10 11 12 |
#import @class WLRRouteRequest; @interface WLRRouteHandler : NSObject //即將handle某一個請求 - (BOOL)shouldHandleWithRequest:(WLRRouteRequest *)request; //根據request取出呼叫的目標檢視控制器 -(UIViewController *)targetViewControllerWithRequest:(WLRRouteRequest *)request; //根據request取出來源的檢視控制器 -(UIViewController *)sourceViewControllerForTransitionWithRequest:(WLRRouteRequest *)request; //開始進行轉場 -(BOOL)transitionWithRequest:(WLRRouteRequest *)request error:(NSError *__autoreleasing *)error; @end |
當WLRRouter物件完成了URL的匹配生成Request,並尋找到Handler的時候,首先會呼叫- (BOOL)shouldHandleWithRequest:(WLRRouteRequest *)request;
,來確定handler是否願意處理,如果願意,則呼叫-(BOOL)transitionWithRequest:(WLRRouteRequest *)request error:(NSError *__autoreleasing *)error;
,內部則通過便利方法獲取targetViewController與SourceViewController,然後進行轉場,核心方法的實現為:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
-(BOOL)transitionWithRequest:(WLRRouteRequest *)request error:(NSError *__autoreleasing *)error{ UIViewController * sourceViewController = [self sourceViewControllerForTransitionWithRequest:request]; UIViewController * targetViewController = [self targetViewControllerWithRequest:request]; if ((![sourceViewController isKindOfClass:[UIViewController class]])||(![targetViewController isKindOfClass:[UIViewController class]])) { *error = [NSError WLRTransitionError]; return NO; } if (targetViewController != nil) { targetViewController.wlr_request = request; } if ([self preferModalPresentationWithRequest:request]||![sourceViewController isKindOfClass:[UINavigationController class]]) { [sourceViewController presentViewController:targetViewController animated:YES completion:nil]; } else if ([sourceViewController isKindOfClass:[UINavigationController class]]){ UINavigationController * nav = (UINavigationController *)sourceViewController; [nav pushViewController:targetViewController animated:YES]; } return YES; } - (BOOL)preferModalPresentationWithRequest:(WLRRouteRequest *)request;{ return NO; } |
這裡根據SourceController的型別進行判斷,其實request物件的資訊足夠可以判斷目標檢視應該如何開啟,從本質上來講,URL的匹配表示式是跟業務強關聯的也是跟UI互動邏輯強關聯的,transitionWithRequest方法實現裡,你大可以繼承一下,然後重寫轉場過程,甚至你可以在這自己設定iOS7自定義的轉場,提供動畫控制器和實現轉場協議的物件,進而可以整體的控制Appp內部的實現。
WLRRegularExpression
該類繼承NSRegularExpression
1 2 3 4 5 6 7 8 |
#import @class WLRMatchResult; @interface WLRRegularExpression : NSRegularExpression //傳入一個URL返回一個匹配結果 -(WLRMatchResult *)matchResultForString:(NSString *)string; //根據一個URL的表示式建立一個WLRRegularExpression例項 +(WLRRegularExpression *)expressionWithPattern:(NSString *)pattern; @end |
該物件主要的功能是將一個URL傳入檢視是否匹配,並且將表示式上宣告的路徑引數從URL上取下來。
比說,我們設定的URL匹配的表示式是: login/:phone([0-9]+),那AppScheme://user/login/138** 這樣的URL應該是匹配,並且將138的手機號取出來,對應到phone上,這個過程必須用到正規表示式的分組提取子串的功能,:phone是約定好的提取子串的值對應的key的名字,其實這個url的正規表示式應該是: /login/([0-9]+)$,那麼WLRRegularExpression物件需要知道需要提取所有子串的key還有將URL匹配的表示式轉換為真正的正規表示式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
-(instancetype)initWithPattern:(NSString *)pattern options:(NSRegularExpressionOptions)options error:(NSError * _Nullable __autoreleasing *)error{ //初始化方法中將URL匹配的表示式pattern轉換為真正的正規表示式 NSString *transformedPattern = [WLRRegularExpression transfromFromPattern:pattern]; //用轉化後的結果初始化父類 if (self = [super initWithPattern:transformedPattern options:options error:error]) { //同時將需要提取的子串的值的Key儲存到陣列中 self.routerParamNamesArr = [[self class] routeParamNamesFromPattern:pattern]; } return self; } //轉換為正規表示式 +(NSString*)transfromFromPattern:(NSString *)pattern{ //將pattern拷貝 NSString * transfromedPattern = [NSString stringWithString:pattern]; //利用:[a-zA-Z0-9-_][^/]+這個正規表示式,將URL匹配的表示式的子串key提取出來,也就是像 /login/:phone([0-9]+)/:name[a-zA-Z-_]這樣的pattern,需要將:phone([0-9]+)和:name[a-zA-Z-_]提取出來 NSArray * paramPatternStrings = [self paramPatternStringsFromPattern:pattern]; NSError * err; //再根據:[a-zA-Z0-9-_]+這個正規表示式,將帶有提取子串的key全部去除,比如將:phone([0-9]+)去除:phone改成([0-9]+) NSRegularExpression * paramNamePatternEx = [NSRegularExpression regularExpressionWithPattern:WLRRouteParamNamePattern options:NSRegularExpressionCaseInsensitive error:&err]; for (NSString * paramPatternString in paramPatternStrings) { NSString * replaceParamPatternString = [paramPatternString copy]; NSTextCheckingResult * foundParamNamePatternResult =[paramNamePatternEx matchesInString:paramPatternString options:NSMatchingReportProgress range:NSMakeRange(0, paramPatternString.length)].firstObject; if (foundParamNamePatternResult) { NSString *paramNamePatternString =[paramPatternString substringWithRange: foundParamNamePatternResult.range]; replaceParamPatternString = [replaceParamPatternString stringByReplacingOccurrencesOfString:paramNamePatternString withString:@""]; } if (replaceParamPatternString.length == 0) { replaceParamPatternString = WLPRouteParamMatchPattern; } transfromedPattern = [transfromedPattern stringByReplacingOccurrencesOfString:paramPatternString withString:replaceParamPatternString]; } if (transfromedPattern.length && !([transfromedPattern characterAtIndex:0] == '/')) { transfromedPattern = [@"^" stringByAppendingString:transfromedPattern]; } //最後結尾要用$符號 transfromedPattern = [transfromedPattern stringByAppendingString:@"$"]; //最後會將/login/:phone([0-9]+)轉換為login/([0-9]+)$ return transfromedPattern; } |
在Matcher物件匹配一個URL的時候
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
-(WLRMatchResult *)matchResultForString:(NSString *)string{ //首先通過自身方法將URL進行匹配得出NSTextCheckingResult結果的陣列 NSArray * array = [self matchesInString:string options:0 range:NSMakeRange(0, string.length)]; WLRMatchResult * result = [[WLRMatchResult alloc]init]; if (array.count == 0) { return result; } result.match = YES; NSMutableDictionary * paramDict = [NSMutableDictionary dictionary]; //遍歷NSTextCheckingResult結果 for (NSTextCheckingResult * paramResult in array) { //再便利根據初始化的時候提取的子串的Key的陣列 for (int i = 1; i<paramResult.numberOfRanges&&i <= self.routerParamNamesArr.count;i++ ) { NSString * paramName = self.routerParamNamesArr[i-1]; //將值取出,然後將key和value放入到paramDict NSString * paramValue = [string substringWithRange:[paramResult rangeAtIndex:i]]; [paramDict setObject:paramValue forKey:paramName]; } } //最後賦值給WLRMatchResult物件 result.paramProperties = paramDict; return result; } |
核心程式碼總共80多行,原始碼大家可以詳閱
WLRRouteMatcher
1 2 3 4 5 6 7 8 |
#import @class WLRRouteRequest; @interface WLRRouteMatcher : NSObject //傳入URL匹配的表示式,獲取一個matcher例項 +(instancetype)matcherWithRouteExpression:(NSString *)expression; //傳入URL,如果能匹配上,則生成WLRRouteRequest物件,同時將各種引數解析好交由WLRRouteRequest攜帶 -(WLRRouteRequest *)createRequestWithURL:(NSURL *)URL primitiveParameters:(NSDictionary *)primitiveParameters targetCallBack:(void(^)(NSError *, id responseObject))targetCallBack; @end |
屬性有如下:
1 2 3 4 5 6 |
//scheme @property(nonatomic,copy) NSString * scheme; //WLRRegularExpression的例項 @property(nonatomic,strong)WLRRegularExpression * regexMatcher; //匹配的表示式 @property(nonatomic,copy)NSString * routeExpressionPattern; |
初始化方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
-(instancetype)initWithRouteExpression:(NSString *)routeExpression{ if (![routeExpression length]) { return nil; } if (self = [super init]) { //將scheme與path部分分別取出 NSArray * parts = [routeExpression componentsSeparatedByString:@"://"]; _scheme = parts.count>1?[parts firstObject]:nil; _routeExpressionPattern =[parts lastObject]; //將path部分當做URL匹配表示式生成WLRRegularExpression例項 _regexMatcher = [WLRRegularExpression expressionWithPattern:_routeExpressionPattern]; } return self; } |
匹配方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
-(WLRRouteRequest *)createRequestWithURL:(NSURL *)URL primitiveParameters:(NSDictionary *)primitiveParameters targetCallBack:(void (^)(NSError *, id))targetCallBack{ NSString * urlString = [NSString stringWithFormat:@"%@%@",URL.host,URL.path]; if (self.scheme.length && ![self.scheme isEqualToString:URL.scheme]) { return nil; } //呼叫self.regexMatcher將URL傳入,獲取WLRMatchResult結果,看是否匹配 WLRMatchResult * result = [self.regexMatcher matchResultForString:urlString]; if (!result.isMatch) { return nil; } //如果匹配,則將result.paramProperties路徑引數傳入,初始化一個WLRRouteRequest例項 WLRRouteRequest * request = [[WLRRouteRequest alloc]initWithURL:URL routeExpression:self.routeExpressionPattern routeParameters:result.paramProperties primitiveParameters:primitiveParameters targetCallBack:targetCallBack]; return request; } |
WLRRouter
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@class WLRRouteRequest; @class WLRRouteHandler; @interface WLRRouter : NSObject //註冊block回撥的URL匹配表示式,可用作內部呼叫 -(void)registerBlock:(WLRRouteRequest *(^)(WLRRouteRequest * request))routeHandlerBlock forRoute:(NSString *)route; //註冊一個WLRRouteHandler對應的URL匹配表示式route -(void)registerHandler:(WLRRouteHandler *)handler forRoute:(NSString *)route; //判斷url是否可以被handle -(BOOL)canHandleWithURL:(NSURL *)url; -(void)setObject:(id)obj forKeyedSubscript:(NSString *)key; -(id)objectForKeyedSubscript:(NSString *)key; //呼叫handleURL方法,傳入URL、原生引數和targetCallBack和完成匹配的completionBlock -(BOOL)handleURL:(NSURL *)URL primitiveParameters:(NSDictionary *)primitiveParameters targetCallBack:(void(^)(NSError *, id responseObject))targetCallBack withCompletionBlock:(void(^)(BOOL handled, NSError *error))completionBlock; |
在實現部分,有三個屬性:
1 2 3 4 5 6 |
//每一個URL的匹配表示式route對應一個matcher例項,放在字典中 @property(nonatomic,strong)NSMutableDictionary * routeMatchers; //每一個URL匹配表示式route對應一個WLRRouteHandler例項 @property(nonatomic,strong)NSMutableDictionary * routeHandles; //每一個URL匹配表示式route對應一個回撥的block @property(nonatomic,strong)NSMutableDictionary * routeblocks; |
在Route掛在Handler和回撥的block的時候:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-(void)registerBlock:(WLRRouteRequest *(^)(WLRRouteRequest *))routeHandlerBlock forRoute:(NSString *)route{ if (routeHandlerBlock && [route length]) { //首先新增一個WLRRouteMatcher例項 [self.routeMatchers setObject:[WLRRouteMatcher matcherWithRouteExpression:route] forKey:route]; //刪除route對應的handler物件 [self.routeHandles removeObjectForKey:route]; //將routeHandlerBlock和route存入對應關係的字典中 self.routeblocks[route] = routeHandlerBlock; } } -(void)registerHandler:(WLRRouteHandler *)handler forRoute:(NSString *)route{ if (handler && [route length]) { //首先生成route對應的WLRRouteMatcher例項 [self.routeMatchers setObject:[WLRRouteMatcher matcherWithRouteExpression:route] forKey:route]; //刪除route對應的block回撥 [self.routeblocks removeObjectForKey:route]; //設定route對應的handler self.routeHandles[route] = handler; } } |
接下來完善handle方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
-(BOOL)handleURL:(NSURL *)URL primitiveParameters:(NSDictionary *)primitiveParameters targetCallBack:(void(^)(NSError *error, id responseObject))targetCallBack withCompletionBlock:(void(^)(BOOL handled, NSError *error))completionBlock{ if (!URL) { return NO; } NSError * error; WLRRouteRequest * request; __block BOOL isHandled = NO; //遍歷routeMatchers中的WLRRouteMatcher物件,將URL傳入物件,看是否能得到WLRRouteRequest物件 for (NSString * route in self.routeMatchers.allKeys) { WLRRouteMatcher * matcher = [self.routeMatchers objectForKey:route]; WLRRouteRequest * request = [matcher createRequestWithURL:URL primitiveParameters:primitiveParameters targetCallBack:targetCallBack]; if (request) { //如果得到WLRRouteRequest物件,說明匹配成功,則進行handler的生命週期函式呼叫或是這block回撥 isHandled = [self handleRouteExpression:route withRequest:request error:&error]; break; } } if (!request) { error = [NSError WLRNotFoundError]; } //在呼叫完畢block或者是handler的生命週期方法以後,回撥完成的completionHandler [self completeRouteWithSuccess:isHandled error:error completionHandler:completionBlock]; return isHandled; } //根據request進行handler的生命週期函式呼叫或者是block回撥 -(BOOL)handleRouteExpression:(NSString *)routeExpression withRequest:(WLRRouteRequest *)request error:(NSError *__autoreleasing *)error { id handler = self[routeExpression]; //self.routeHandles和self.routeblocks拿到route對應的回撥block或者是handler例項 if ([handler isKindOfClass:NSClassFromString(@"NSBlock")]) { WLRRouteRequest *(^blcok)(WLRRouteRequest *) = handler; //呼叫回撥的block WLRRouteRequest * backRequest = blcok(request); //判斷block裡面是否消費了此request,如果沒有而目標設定了目標回撥targetCallBack,那麼在此進行預設回撥 if (backRequest.isConsumed==NO) { if (backRequest.targetCallBack) { dispatch_async(dispatch_get_main_queue(), ^{ backRequest.targetCallBack(nil,nil); }); } } return YES; } else if ([handler isKindOfClass:[WLRRouteHandler class]]){ //拿到url對應的handler物件後,先呼叫handler的shouldHandleWithRequest方法,如果返回YES,則呼叫進行轉場的transitionWithRequest方法 WLRRouteHandler * rHandler = (WLRRouteHandler *)handler; if (![rHandler shouldHandleWithRequest:request]) { return NO; } return [rHandler transitionWithRequest:request error:error]; } return YES; } |
以上我們可以看到,Route將匹配的邏輯單獨封裝到WLRRouteMatcher物件中,將匹配後的結果生成WLRRouteRequest例項以攜帶足夠完整的資料,同時將真正處理檢視控制器的轉場或者是元件的載入或者是未來可能擴充的handle業務封裝到WLRRouteHandler例項中,匹配邏輯對應的處理邏輯乾淨分離,匹配邏輯可單獨塑造業務匹配,處理邏輯可以通過繼承擴充套件或者沖洗WLRRouteHandler的生命週期函式來更好的處理回撥業務。如果WLRRouteHandler不能提供足夠多的擴充套件性,則可以使用block回撥最大限度的進行擴充套件。
以上,就是路由部分的整體實現。
轉場的擴充套件
在WLRRouteHandler中,其實我們可以單獨控制路由經過的頁面跳轉的轉場。
1 2 3 4 5 6 7 |
-(UIViewController *)targetViewControllerWithRequest:(WLRRouteRequest *)request{ } -(UIViewController *)sourceViewControllerForTransitionWithRequest:(WLRRouteRequest *)request{ } -(BOOL)transitionWithRequest:(WLRRouteRequest *)request error:(NSError *__autoreleasing *)error{ } |
這樣的生命週期函式是不是很像UIViewControllerContextTransitioning轉場上下文的協議的設定?- (nullable __kindof UIViewController *)viewControllerForKey:(UITransitionContextViewControllerKey)key;
方法使上下文提供目標控制器和源控制器,其實在handler中你完全可以自定義一個子類,在transitionWithRequest方法裡,設定遵守UIViewControllerTransitioningDelegate的代理,然後在此提供遵守 UIViewControllerAnimatedTransitioning的動畫控制器,然後自定義轉場上下文,實現自定義UI轉場,而對應的匹配邏輯是與此無關的,我們就可以在路由曾控制全域性的頁面轉場效果。對自定義轉場不太熟悉的同學請移步我之前的文章:
ContainerViewController的ViewController 轉場
路由的安全
有兩個方面可以去做
- WLRRouteHandler例項中,
-(BOOL)shouldHandleWithRequest:(WLRRouteRequest *)request
中可以檢測request中的引數,比方說效驗source或者是效驗業務引數完整等 - WLRRouter例項中handleURL方法,將在隨後的WLRRoute的0.0.2版本中加入中介軟體的支援,就是在找到handler之前,將按照中介軟體註冊的順序回撥中介軟體,而我們可以在中介軟體中實現風控業務、認證機制、加密驗籤等等
路由的效率
目前我們實現的路由是一個同步阻塞型的,在處理併發的時候可能會出現一些問題,或者是在註冊比較多的route表示式以後,遍歷和匹配的過程會損耗效能,比較好的實現方式是,將Route修改成非同步非阻塞型的,但是API全部要換成非同步API,起步我們先把同步型的搞定,隨後慢慢提供非同步版本的路由~
路由的使用
在大部分App實踐MVVM架構或者更為複雜的VIPER架構的時候,除了迫切需要一個比較解耦的訊息傳遞機制,如何更好的剝離目標實體的獲取和配合UIKit這一層的轉場邏輯是一項比較複雜的挑戰,路由實際上是充當MVVM的ViewModel中比較解耦的目標獲取邏輯和VIPER中Router層,P與V的呼叫全部靠Router轉發。
在實施以元件化為目的的工程化改造中,如何抽離單獨業務為元件,比較好的管理業務與業務之間的依賴,就必須使用一個入侵比較小的Route,WLRRoute入侵的地方在於WLRRouteHandler的transitionWithRequest邏輯中,通過一個UIViewController的擴充套件,給 targetViewController.wlr_request = request;設定了WLRRouteRequest物件給目標業務,但雖然如此,你依舊可以重寫WLRRouteHandler的transitionWithRequest方法,來構建你自己引數傳遞方式,這一點完全取決於你如何更好的使得業務無感知而使用路由。
最後附上程式碼地址:
喜歡的來個星吧…
https://github.com/Neojoke/WLRRoute