iOS組件化方案

1、蘑菇街url-block方案

  這是蘑菇街中應用的一種頁面間調用的方式,經過在啓動時註冊組件提供的服務,把調用組件使用的url和組件提供的服務block對應起來,保存到內存中。在使用組件的服務時,經過url找到對應的block,而後獲取服務。安全

  具體實現代碼以下:atom

//Mediator.m 中間件
@implementation Mediator
typedef void (^componentBlock) (id param);
@property (nonatomic, storng) NSMutableDictionary *cache
- (void)registerURLPattern:(NSString *)urlPattern toHandler:(componentBlock)blk {
  [cache setObject:blk forKey:urlPattern];
}
- (void)openURL:(NSString *)url withParam:(id)param {
  componentBlock blk = [cache objectForKey:url];
  if (blk) blk(param); } @end

1.先在Mediator中註冊url-block

#import "Mediator.h"
#import "WRBookDetailViewController.h"
+ (void)initComponent {
  [[Mediator sharedInstance] registerURLPattern:@"weread://bookDetail" toHandler:^(NSDictionary *param) {
    WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
    [[UIApplication sharedApplication].keyWindow.rootViewController.navigationController pushViewController:detailVC animated:YES];
  }]; }

2.經過url在中間件中就能找到對應的block,並執行跳轉

#import "Mediator.h"
+ (void)gotoDetail:(NSString *)bookId {
  [[Mediator sharedInstance] openURL:@"weread://bookDetail" withParam:@{@"bookId": bookId}]; }

 2、 蘑菇街protocol-class方案

  針對url-block方案沒法傳遞非正規參數,新增一種protocol-class方案url

//ProtocolMediator.m 中間件
@implementation ProtocolMediator
@property (nonatomic, storng) NSMutableDictionary *protocolCache
- (void)registerProtocol:(Protocol *)proto forClass:(Class)cls {
  NSMutableDictionary *protocolCache;
  [protocolCache setObject:cls forKey:NSStringFromProtocol(proto)];
}
 
- (Class)classForProtocol:(Protocol *)proto {
  return protocolCache[NSStringFromProtocol(proto)];
}
@end

 每個組件都有一個公共Protocol文件,定義了每個組件對外提供的接口spa

//ComponentProtocol.h
@protocol BookDetailComponentProtocol <NSObject>
- (UIViewController *)bookDetailController:(NSString *)bookId;
- (UIImage *)coverImageWithBookId:(NSString *)bookId;
@end
 
@protocol ReviewComponentProtocol <NSObject>
- (UIViewController *)ReviewController:(NSString *)bookId;
@end


//BookDetailComponent 組件
#import "ProtocolMediator.h"
#import "ComponentProtocol.h"
#import "WRBookDetailViewController.h"
+ (void)initComponent{
  [[ProtocolMediator sharedInstance] registerProtocol:@protocol(BookDetailComponentProtocol) forClass:[self class];
}
 
- (UIViewController *)bookDetailController:(NSString *)bookId {
  WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
  return detailVC;
}
 
- (UIImage *)coverImageWithBookId:(NSString *)bookId {
  ….
}

 最後調用者經過 protocol 從 ProtocolMediator 拿到提供這些方法的 Class,再進行調用:code

//WRReadingViewController.m 調用者
//ReadingViewController.m
#import "ProtocolMediator.h"
#import "ComponentProtocol.h"
+ (void)gotoDetail:(NSString *)bookId {
  Class cls = [[ProtocolMediator sharedInstance] classForProtocol:BookDetailComponentProtocol];
  id bookDetailComponent = [[cls alloc] init];
  UIViewController *vc = [bookDetailComponent bookDetailController:bookId];
  [self.navigationController pushViewController:vc animated:YES];
}

 3、target-action方案

  1.先設置一箇中間件公共類

@interface CTMediator : NSObject

+ (instancetype)sharedInstance;

// 遠程App調用入口
- (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;
// 本地組件調用入口
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;
- (void)releaseCachedTargetWithTargetName:(NSString *)targetName;

@end

//
//  CTMediator.m
//  CTMediator
//
//  Created by casa on 16/3/13.
//  Copyright © 2016年 casa. All rights reserved.
//

#import "CTMediator.h"

@interface CTMediator ()

@property (nonatomic, strong) NSMutableDictionary *cachedTarget;

@end

@implementation CTMediator

#pragma mark - public methods
+ (instancetype)sharedInstance
{
    static CTMediator *mediator;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mediator = [[CTMediator alloc] init];
    });
    return mediator;
}

/*
 scheme://[target]/[action]?[params]
 
 url sample:
 aaa://targetA/actionB?id=1234
 */

- (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion
{
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    NSString *urlString = [url query];
    for (NSString *param in [urlString componentsSeparatedByString:@"&"]) {
        NSArray *elts = [param componentsSeparatedByString:@"="];
        if([elts count] < 2) continue;
        [params setObject:[elts lastObject] forKey:[elts firstObject]];
    }
    
    // 這裏這麼寫主要是出於安全考慮,防止黑客經過遠程方式調用本地模塊。這裏的作法足以應對絕大多數場景,若是要求更加嚴苛,也能夠作更加複雜的安全邏輯。
    NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
    if ([actionName hasPrefix:@"native"]) {
        return @(NO);
    }
    
    // 這個demo針對URL的路由處理很是簡單,就只是取對應的target名字和method名字,但這已經足以應對絕大部份需求。若是須要拓展,能夠在這個方法調用以前加入完整的路由邏輯
    id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO];
    if (completion) {
        if (result) {
            completion(@{@"result":result});
        } else {
            completion(nil);
        }
    }
    return result;
}

- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
    
    NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
    
    id target = self.cachedTarget[targetClassString];
    if (target == nil) {
        Class targetClass = NSClassFromString(targetClassString);
        target = [[targetClass alloc] init];
    }
    
    SEL action = NSSelectorFromString(actionString);
    
    if (target == nil) {
        // 這裏是處理無響應請求的地方之一,這個demo作得比較簡單,若是沒有能夠響應的target,就直接return了。實際開發過程當中是能夠事先給一個固定的target專門用於在這個時候頂上,而後處理這種請求的
        return nil;
    }
    
    if (shouldCacheTarget) {
        self.cachedTarget[targetClassString] = target;
    }
    
    if ([target respondsToSelector:action]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
    } else {
        // 這裏是處理無響應請求的地方,若是無響應,則嘗試調用對應target的notFound方法統一處理
        SEL action = NSSelectorFromString(@"notFound:");
        if ([target respondsToSelector:action]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
        } else {
            // 這裏也是處理無響應請求的地方,在notFound都沒有的時候,這個demo是直接return了。實際開發過程當中,能夠用前面提到的固定的target頂上的。
            [self.cachedTarget removeObjectForKey:targetClassString];
            return nil;
        }
    }
}

- (void)releaseCachedTargetWithTargetName:(NSString *)targetName
{
    NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    [self.cachedTarget removeObjectForKey:targetClassString];
}

#pragma mark - getters and setters
- (NSMutableDictionary *)cachedTarget
{
    if (_cachedTarget == nil) {
        _cachedTarget = [[NSMutableDictionary alloc] init];
    }
    return _cachedTarget;
}

@end

    2.每一個組件建立一個Category,在其中實現當前組件的跳轉實現component

#import <CTMediator/CTMediator.h>
#import <UIKit/UIKit.h>

@interface CTMediator (TAConfirmOrder)

- (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete;

@end

#import "CTMediator+TAConfirmOrder.h"

@implementation CTMediator (TAConfirmOrder)

- (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete
{
    NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
    params[@"goodsId"] = goodsId;
    params[@"goodsName"] = goodsName;
    params[@"completeBlock"] = confirmComplete;
    return [self performTarget:@"TAConfirmOrder" action:@"ConfirmOrderViewController" params:params shouldCacheTarget:NO];
}

@end

 3.經過中間件調用

UIViewController *confirmOrderVC = [[CTMediator sharedInstance] confirmOrderViewControllerWithGoodsId:self.goodsId  goodsName:self.goodsName  ConfirmComplete:^{orm

        //...中間件

   }];blog

相關文章
相關標籤/搜索