# GMediator
## 构想
App 的终极形态是由多个业务组件组成，每个业务组件依赖若干其它Pod库（net, base等），业务组件之间没有直接依赖，每个业务组件可以单独运行。

鉴于业务的复杂性，A业务依赖B业务，B业务也依赖A业务的情况是完全存在的，这种情况看着就感觉会发生不好的事，最直观能想到的一个结果是B组件public方法可能存在bug fix，A不得不升级podfile

为了解决这种情况的发生，创造了GMediator+Target+GMediator_Cateogry，比如GMediator_Conversation就是一个业务GMediator_Cateogry。



## 下面以用户版与GMConversation来说明

其实这两者就是相互依赖的，不过下面只以用户版调用GMConversation来说明

### 角色说明

Header1 | Header2
------- | -------
GMConversation | 私信客服业务组件
GMediator_Conversation | 向外提供GMConversation业务接口，不依赖任务业务组件，只依赖GMediator，单独成Pod库
GMediator|接收 GMediator_Conversation 发来的消息，利用约定的命名规则，使用objc runtime可以找到GMConversation组件的Target及selector，所以 GMediator 不依赖任何东西
Target|在 GMConversation 组件里实现具体业务。比如 GMConversation 中的 Target_GMConversation 类

### 调用顺序

```flow
st=>start: Gengmei
e=>end: 逐层返回该业务的结果
op1=>operation: GMediator_Conversation 提供业务接口
op2=>operation: GMediator 将消息转发到 GMConversation 业务的 Target
op3=>operation: Target 收到消息、处理
st->op1->op2->op3->e
```

```
// 依赖 
Gengmei	 |-> GMediator_Conversation
GMediator_Conversation  |-> GMediator
GMediator |-> 不依赖任何东西
```


## 使用注意
其中1，2，3步需要在业务组件项目中实现，4需要单独创建一个Pod库实现。
### 1. 添加默认的NotFoundTarget
业务组件项目需要实现一个 NotFoundTarget 类，实现一个 notFound 方法。可以直接使用下面的代码

``` swift
@objc(Target_NotFound)
// swiftlint:disable type_name
class Target_NotFound: NSObject {
    func notFound() -> UIViewController {
        return UIViewController()
    }
}
```

### 2. 修改info.plist
这一步为了获取正确的Module名。如果Target实现为swift，那么需要用到Module名
使用source code方法打开info.plist，添加下面的代码。GMediator 在查找swift Target时会用到该值

``` xml
<key>CFProductModuleName</key>
<string>$(PRODUCT_MODULE_NAME)</string>
```

如果没有该值，会获取 CFBundleName，一般情况下 CFBundleName 会在 info.plist 里指向 build setting 中的 product name，而 product name 常常与 module name 是一样的。
当然有例外，此时就突显出设置Module的意义了
参考：[Using Swift with Cocoa and Objective-C 文档中的 Naming Your Product Module 章节](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-ID138)
     

## 3. Target 实现
- 类名：Target_XXXX。
- 类名前需要加注释`// swiftlint:disable type_name`以关掉swiftLint在此处的检查。
- 方法：必须为==**实例方法**==
- Target_Gengmei 中原来有的方法pushToXXXX作为历史遗留问题，暂时不用改
- 方法命名方式：Action_业务名词 [+ withParams] 例如

```
// OC: 
Action_orderDetailWithParams:(NSDictionary *)params 
Action_orderDetail

// swift
// swiftlint:disable type_name
class Target_Gengmei: NSObject {
	 // swift方法
    func Action_welfareDetail(dic: TargetDicType) -> UIViewController? {
        guard let welfareId = dic["welfareId"] as? String else { return nil }
        let c = WMWelfareDetailViewController()
        c.viewModel.welfareId = welfareId
        return c
    }
```


### 4. 添加扩展
- 使用==**类方法**==
- 方法名：业务名词 + 介词 + 参数名... 

```
+ (UIViewController *)welfareDetailWithWelfareId:(NSString *)welfareId;
+ (UIViewController *)viewPictureFromURLs:(NSArray<NSString *> *)images index:(NSInteger)index;
```

- 如果某个扩展实现为swift，且某方法的某参数是闭包，那么这个方法实现需要额外的代码将该闭包转为AnyObject类型。因为 GMediator.h 中的下面这个方法的parmas参数类型决定了这个字典中的value必须都是AnyObject。参考Demo中的`GMediator+AlphaBlock.swift`

```
// in GMediator.h
- (nullable id)performOnTarget:(NSString *)targetName action:(NSString *)actionName params:(nullable NSDictionary<NSString *, id> *)params;
```

```
static func alphaBlock(block: MyClosure) -> UIViewController {
    // 扩展实现中需要额外添加的代码
    let blockObject: AnyObject = unsafeBitCast(block, AnyObject.self)
    return GMediator.sharedInstance().controller(fromTarget: "AlphaBlock", action: "fetchA", params: ["block": blockObject])
}
```

### 5. Target 方法中字典key约束
- 对于Target方法的参数字典中的key做如下约束：参考更美协议的参数key命名方式
- 如果在 Target_Gengmei 中已经有了该方法，直接使用

通过以上两点，与更美协议达成统一性


## 主要历史

时间 | 备注 | 作者
------- | ------- | -------
2016-7月末 | 创建初始版本 | 邢天宇
2016-8-8 | 使用注意 | 汪洋
2016-8-18| 添加构想及说明 | 汪洋
2016-8-29| 精简Pod库，重新组织Demo项目，优化performActionWithUrl中的逻辑 | 汪洋
2016-8-30| 优化README 中的“调用顺序”图 | 汪洋

## License
更美内部使用


