可以看下面两篇文章:
如何创建私有 CocoaPods 仓库
CocoaPods创建私有Pods
创建步骤总结:
- 创建一个spec仓库,用来存放私有仓库的spec文件
- 将这个私有的spec仓库,添加到CocoaPods
pod repo add REPO_NAME SOURCE_URL
- 生成代码库的spec文件,打tag,并push到私有spec仓库
pod repo push REPO_NAME SPEC_NAME.podspec
- 使用的时候,要在Podfile文件中同时添加本地私有源和官方源。如果没有添加本地私有源,它默认是用官方的repo,这样找不到本地的Pod;如果只是设置了本地私有源,就不会再去官方源中查找。
iOS 组件化方案探索
CTMediator
在现有工程中实施基于CTMediator的组件化方案
然后,iOS组件化方案调研里有收集很多参考资料,有空时要系统的阅读。
读了在现有工程中实施基于CTMediator的组件化方案一文后,有下述问题:
- 可否将A_Category和B_Category合并为一个Pod?
答:不能,除非这两个pod彼此不能拆解。但是如果彼此不能拆解的话,就不应该出现两个category,应该只有一个才合理。
2)B_Category提供的服务和Target_B提供的服务是一样的,能不能把B_Category直接放到B的模块里替代Target_B?
答:不要这么做。category是业务无关的,target是业务相关的。外部只会通过category来调用功能,这样能够达到两个效果:1.某组件即使缺少依赖,也能编译通过。2.在Podfile中添加被依赖的组件后,不用修改任何代码,就能够正常调用被依赖的组件的功能。
这样才能做到完全解耦。 - 组件划分的标准是什么?
答:考虑两点,1.这一部分是否能够自治,且相对独立。2.这一部分是否会被多个调用者调度。只要这二者有一个条件满足,那就会把这部分组件化出来,即使它只有一个对象不到50行代码。
组件化更多的是针对横向依赖做依赖下沉。业务相对于服务之间的依赖属于纵向依赖,把服务作为普通Pod引入即可。业务和业务之间是横向依赖,必须组件化。
-
工程中的公用图片资源在组件化时应该怎么处理呢? 答:我们是单独一个子工程,里面只有图片。然后在其它使用图片的子工程里,只把这个图片子工程写入Podfile,而不写入podspec的dependency里面,这样能确保调试的时候有图片,组件发版的时候不带图片。然后在主工程的Podfile里面同样也写入图片子工程,这样就好了。
-
组件的负责团队一般就只工作在组件工程里,里面除了组件自身,还有单元测试代码,一般保证自己工作正常就可以了。需要联调时,可以在Podfile里引入依赖的组件。
假如主APP调用某业务A,那么需要以下组成部分:
-
CTMediator类,该类提供了函数
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;
这个函数可以根据targetName生成对象,根据actionName构造selector,然后可以利用performSelector:withObject:方法,在目标上执行动作。 -
业务A的实现代码,另外要加一个专门的类,用于执行Target Action
类的名字的格式:Target_%@
,这里就是Target_A。
这个类里面的方法,名字都以Action_
开头,需要传参数时,都统一以NSDictionary*的形式传入。
CTMediator类会创建Target类的对象,并在对象上执行方法。 -
业务A的CTMediator扩展
扩展里声明了所有A业务的对外接口,参数明确,这样外部调用者可以很容易理解如何调用接口。
在扩展的实现里,对Target, Action需要通过硬编码进行指定。由于扩展的负责方和业务的负责方是相同的,所以这个不是问题。
Router的缺点:
- 在组件化的实施过程中,注册URL并不是充分必要条件。组件是不需要向组件管理器注册URL的,注册了URL之后,会造成不必要的内存常驻。注册URL的目的其实是一个服务发现的过程,在iOS领域中,服务发现的方式是不需要通过主动注册的,使用runtime就可以了。另外,注册部分的代码的维护是一个相对麻烦的事情,每一次支持新调用时,都要去维护一次注册列表。如果有调用被弃用了,是经常会忘记删项目的。runtime由于不存在注册过程,那就也不会产生维护的操作,维护成本就降低了。 由于通过runtime做到了服务的自动发现,拓展调用接口的任务就仅在于各自的模块,任何一次新接口添加,新业务添加,都不必去主工程做操作,十分透明。
- 在iOS领域里,一定是组件化的中间件为openURL提供服务,而不是openURL方式为组件化提供服务。如果在给App实施组件化方案的过程中是基于openURL的方案的话,有一个致命缺陷:非常规对象(不能被字符串化到URL中的对象,例如UIImage)无法参与本地组件间调度。
- 在本地调用中使用URL的方式其实是不必要的,如果业务工程师在本地间调度时需要给出URL,那么就不可避免要提供params,在调用时要提供哪些params是业务工程师很容易懵逼的地方。
- 为了支持传递非常规参数,蘑菇街的方案采用了protocol,这个会侵入业务。由于业务中的某个对象需要被调用,因此必须要符合某个可被调用的protocol,然而这个protocol又不存在于当前业务领域,于是当前业务就不得不依赖public Protocol。这对于将来的业务迁移是有非常大的影响的。
CTMediator的优点:
- 调用时,区分了本地应用调用和远程应用调用。本地应用调用为远程应用调用提供服务。
- 组件仅通过Action暴露可调用接口,模块与模块之间的接口被固化在了Target-Action这一层,避免了实施组件化的改造过程中,对Business的侵入,同时也提高了组件化接口的可维护性。
- 方便传递各种类型的参数。
Native的缺点:
App的发版周期偏长,有时无法跟上产品的更新节奏
灵活性差,如果有圈套的方案变更,需要发版才能解决
如果存在bug,无法在当前版本进行修复
需要根据不同的平台写不同的代码
H5的优点:
页面可以实时在服务端进行修改,灵活性很强,避免了Native发版周期带来的时间成本
H5的缺点:
弱网情况下体验较差
流量消耗较大
访问原生系统的能力受到限制
通常的经验是:对于一些比较稳定的业务,对用户体验要求较高的,可以选择Native开发。而对于一些业务变更比较快,处在不断试水的过程,而且不涉及调用文件系统和硬件调用的业务我们可以选择H5开发。
Composition描述的是'part-of'的关系,如下图:
如果我们对汽车进行建模,那么说引擎是车的一部分,是比较合适的。在组合的情况下,部分的生命周期,由整体所管理。也就是说,当车被销毁的时候,引擎也会跟着被销毁。用C#代码表示就是:
public class Engine
{
. . .
}
public class Car
{
Engine e = new Engine();
.......
}
Aggregation描述的是'has-a'的关系,例如,一个客户拥有一个地址。客户不存在的时候,对应的地址还是会继续存在的。如下图:
用C#代码表示就是:
public class Address
{
. . .
}
public class Person
{
private Address address;
public Person(Address address)
{
this.address = address;
}
. . .
}
Swift语言可以对Protocol增加扩展,提供缺省的行为。这种方式看起来类似于其它语言中的基类或是抽象类,但它的好处是:
-
类型可以符合多个协议,从每个协议都可以获得缺省的行为。与多继承不同,协议扩展并不引入额外的状态。
-
类,结构体,枚举类型都可以声明符合一个协议;继承只能局限于类类型。
-
扩展标准库中的协议
protocol Bird: CustomStringConvertible
一个类型符合CustomStringConvertible协议,意味着需要有一个String类型的description
属性,但是这是否意味着,我们需要为当前的和未来的每个Bird类型,添加这个属性呢?协议扩展允许我们更加容易的处理这个情况。
extension CustomStringConvertible where Self: Bird {
var description: String {
return canFly ? "I can fly" : "Guess I’ll just sit here :["
}
}
- 定义集合类型时,指定元素类型为协议类型,那么集合中可以同时装入值类型和类类型的对象。
在这个设计模式里,核心是ViewModel,它是一种特殊类型的model,代表了应用中UI的状态。它包含如下内容:
- 每个UI控件的一些属性。例如,text field控件的当前文本,某个button是否是enable状态。
- 视图可以执行的动作,例如按钮点击或者是手势。
将ViewModel想象为视图的模型,会比较容易理解。
MVVM模式中,三个组件的关系比MVC模式的要简单,有下面的严格规则:
- 视图引用ViewModel,但反向不成立。
- ViewModel引用Model,但反向不成立。
如果违背了上面两条规则,那么就是错误的MVVM实施行为。
这种模式的好处:
- 轻量级的视图(控制器), 所有的UI逻辑都位于ViewModel中。
- 易测试性。可以在没有视图的情况下,运行整个应用。