iOS的组件化思路分享

Cocoapods

iOS的组件化,一直都是一个理念,很多大公司一直在强调却没有具体可行的或简单可行的方案。所以分享下大概思路。

框架思路

框架说明:

  1. 继续集成:一个主工程(壳工程),包含所有的内容(整个项目),用于发包或打包测试。
  2. 基础组件: 不依赖其他任何组件,独立完成功能。主要有:*与业务无法的功能(如string或data的加密,category的封装) *对第三方库的封装(如AFNetworking,SDWebImage的封装)
  3. 业务公用组件:依赖基础组件或UIKit等系统组件,创建业务共同使用的功能(如分享,支付,网络访问)
  4. 中间组件:连接业务公用组件和业务组件,及业务组件之间的互相调用。(如Mediator的组件)
  5. 业务组件:单独的业务功能,不依赖其他业务组件。

大致的框架思路,其中会碰到一个颗粒度的问题,将颗粒度设定过大,则组件之间的耦合性过大,颗粒度设定过小,则将产生很多的组件和中间组件,组件的管理成本过大。(具体问题需要具体分析)

一、 Cocoapods的组件管理

1. pod spec文件创建

spec文件说明

Pod::Spec.new do |spec| 
  spec.name = 'Reachability' spec.version = '3.1.0'
  spec.license = { :type => 'BSD' } 
  spec.homepage = 'https://github.com/tonymillion/Reachability' 
  spec.authors = { 'Tony Million' => 'tonymillion@gmail.com' } 
  spec.summary = 'ARC and GCD Compatible Reachability Class for iOS and OS X.' 
  spec.source = { :git => 'https://github.com/tonymillion/Reachability.git', :tag => 'v3.1.0' } 
  spec.source_files = 'Reachability.{h,m}' 
  spec.framework = 'SystemConfiguration'
end

根据内容编写项目的信息,小的细节可以学习Git上支持Cocoapods的项目,查看项目中*.spec文件。
如:
(1). 添加资源文件使用 s.resource_bundles
(2). 需要添加多个文件,使用数组s.source_files = ['ThirdSdk/Classes/**/*.{h,m}', 'ThirdSdk/Classes/**/**/*.{h,m}']

2. LICENSE和README.md文件

编写许可证和说明的文件。最好参考官方的写法,不然检测不过。

3.创建一个Example工程

关键一个Example的工程,在Podfile中采用直接引用Module文件进行组件的开发。方便对单个组件进行调试
pod 'ThirdSdk', :path => '../'

组件直接引用

可以直接对Module进行修改,添加,删除等。

4.本地资料库(cocoapods镜像)创建

新建一个Git库,取名Specs用来保存本地的所有私有库。
使用pod repo add [name] [Git地址] 添加一个私有Cocoapods Specs镜像。
使用pod repo push REPO [NAME.podspec] 将私有组件推送到私有第三方库镜像。

5.依赖第三方库

在Podfile文件中可以添加依赖的第三方库,包括Cocoapods和私有本地的库。

platform :ios, '7.0'

source 'git@code.*.com:ios/Specs.git'

source 'https://github.com/CocoaPods/Specs.git'

target 'ThirdSdk_Example' do
  pod 'ThirdSdk', :path => '../'
  
  #cocoapods specs
  pod 'AliyunOSSiOS'
  
  #local specs
  pod 'WebService'
end

其中
(1)source 'git@code.*.com:ios/Specs.git'是申请引用本地指定的资料地址。
(2)source 'https://github.com/CocoaPods/Specs.git'是cocoapods官方的第三方库资料地址。
(3)pod 'AliyunOSSiOS'引用的是Cocoapods的第三方库。
(4)pod 'WebService'引用的是私有的第三方库。

二、组件之间的依赖

(1)对于基础组件的依赖,采用Pod 直接导致,在代码中进行import头文件。这个方式会导致基础组件的耦合性比较高。可以将组件的颗粒度设定的小一些,尽量抽取出不依赖其他组件的独立组件。再将这些独立的组件打包成一个较大的组件,方便管理。如Podfile中

#local specs
  pod 'Account'
  pod 'AppPod'
  pod 'WebService'

其中的AppPod就是集合,包含很多独立的基础组件。

(2)如果是业务组件,在不同的业务之间,一定要避免组件间耦合,采用Mediator组件作为中间件,降低耦合。 具体可以参考iOS应用架构谈 开篇 提供的Demo编写。

三、主工程

主工程(壳工程)引用所有的组件,并实现应用的工程。

iOS应用框架及Cocoapods内容的参考:
http://casatwy.com/iosying-yong-jia-gou-tan-kai-pian.html
http://www.cocoachina.com/ios/20160108/14916.html
http://gracelancy.com/blog/2016/01/06/ape-ios-arch-design/
http://blog.csdn.net/xyxjn/article/details/42527341
http://pingguohe.net/2015/11/24/Navigator-and-Rewrite.html
http://studentdeng.github.io/blog/2013/09/13/cocoapods-tutorial/
http://ishalou.com/blog/2012/10/16/how-to-create-a-cocoapods-spec-file/
http://stackoverflow.com/questions/25470415/multiple-private-repo-cocoapods
http://www.cnblogs.com/Mr-ios/p/5310666.html

补充

1. 使用pod install 更新失败的处理

在使用Cocoapods管理代码的时候,如果在GitHut上更新了代码,希望在project中执行pod install 来刷新代码,那么需要
删除Pods和Podfile.lock文件
然后在执行pod install 来刷新代码

使用pod update进行repo的更新

多次安装pod install 会出现Build Phase里多个Check Pods Manifest.lock的选项,这些选项可以删除。


删除这个目录下的缓存文件,进行第三方库文件的重新获取
/Users/*/Library/Caches/CocoaPods/Pods/*

因为有可能因为版本号,没有增加,在Pods目录下的VERSION保存了这个版本的第三方库,那么将直接返回已经缓存的文件。所以当私有组件有代码修改后,则可以清空这个文件夹下的目录,来进行私有组件的更新。👍👍👍(研究了很久才发现这个终极解决方法)


2. Pod中生成文件夹形式或创建子组件

在*.podspec文件中,根据功能创建不同的子组件,如AFN中的

s.subspec 'Security' do |ss|

ss.source_files = 'AFNetworking/AFSecurityPolicy.{h,m}'

ss.public_header_files = 'AFNetworking/AFSecurityPolicy.h'

ss.frameworks = 'Security'

end

生成的效果


文件夹

这种方式也是子组件的方法,在pod中可以直接引用AFSecurityPolicy的组件,如pod 'AFNetworking/Security' 此时仅仅引用的是Security组件,而不是整个AFN组件

单个Security组件

在开发是看到的组件,文件夹会比较乱,当其他组件pod后,看到的文件夹就如AFNetworking一样的效果。


3. *.podspec的dependency使用

组件可以依赖其他的第三方库,如s.dependency 'JTObjectMapping'AFNetworking等。
同时也可以依赖私有的第三方库
(1)使用pod repo add [name] [Git地址] 添加一个私有Cocoapods Specs镜像。
(2)使用pod repo push REPO [NAME.podspec] 将私有组件推送到私有第三方库镜像。
(3)在组件中就可以将已经上传的组件可以用来依赖了。

4. ThirdSdk组件

在ThirdSdk组件中,不能包含业务或项目相关的类,不然会导致互相依赖。仅仅包含第三方库的代码,可以降低耦合性。
如果能用Cocoapods管理的第三方库,尽量使用Cocoapods吧。

5. 加载静态库和framework
ss.vendored_frameworks = 'ThirdSdk/AlipaySDK/AlipaySDK.framework'
ss.vendored_libraries = 'ThirdSdk/AlipaySDK/libcrypto.a', 'ThirdSdk/AlipaySDK/libssl.a'

a文件不是一个source_files。

6. runtime不能返回Bool等基础数据类型
return [target performSelector:action withObject:params];

如果返回的值是Bool或其他的基础数据类型,那么将会Crash

需要将返回值设置为NSObject对象类型或nil。

7. 组件中获取xib,png等resource时,bundle重新设置
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    NSString *bundlePath = [bundle pathForResource:@"StoreLocation" ofType:@"bundle"];
    if (bundlePath) {
        bundle = [NSBundle bundleWithPath:bundlePath];
    }

其中StoreLocation是与podSpec中的s.resource_bundles里设置的保持一致。

s.resource_bundles = {
    'StoreLocation' => ['StoreLocation/Assets/**/*', 'StoreLocation/Classes/Address/*.{xib}'']
  }
8. 图片和URL的处理

1、每一个组件需要用到的图片,都放到组件的Assets文件夹中。
在iOS8及更高系统,组件中Xib访问image会从组件的Assets中读取。但是iOS7系统,Xib还是从[NSBundle mainBundle]的Assets中读取文件。
2、URL 不单独放到一个头文件中,放入组件的ViewModel中。

9. TAG的刷新

当有多人合作开发,TAG又一直维持在1.0.0时,那么需要每次更新所有的TAG。

更新标签

需要选中“抓取并在本地存储所有标签”。

10. push 组件 *.podspec 文件
pod repo push Specs AppPod.podspec --allow-warnings

推送时,如果一直报错,修护了Error还是一直报同样的Error,这时可以执行下pod cache clean --all 清空缓存。

11. 不同configurations设置宏

当添加一个Ad-hoc的configuration,这个也需要设置DEBUG这个宏,并且在组件中也用到了DEBUG的宏定义,那么需要对所有的组件进行DEBUG的设置。方法如下

#设置Debug参数
post_install do |installer|

  installer.pods_project.targets.each do |target|

    target.build_configurations.each do |config|

      if config.name == 'Debug' 
       config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) DEBUG=1'
      
      end
      
      if config.name == 'Ad-hoc' 
       config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] = '$(inherited) DEBUG=1'
      
      end

end

end

end
#设置参数完成
12. 更新OpenSSL失败

OpenSSL更新失败,可以删除/tmp/openssl 文件夹,重新尝试。

13. OpenSSL 的使用

在使用支付宝SDK时,会有引入OpenSSL文件夹,其中文件中使用#include <openssl/e_os2.h>。将SDK放入组件中,会报错,提示找不到文件。
解决方法: 在*.podspec文件中 添加 ss.header_dir = "openssl"用来添加头文件夹openssl。

14. ARC 和 MRC 兼容

将使用MAC的放入subspec

s.subspec 'JTObjectMapping' do |ss|
    ss.source_files = 'StoreBase/Classes/JTObjectMapping/*.{h,m}'
    ss.public_header_files = 'StoreBase/Classes/JTObjectMapping/*.h'

    ss.requires_arc = false
  end

因为JTObjectMapping第三方库使用的MRC所以特殊声明ss.requires_arc = false. 在主spec中使用的ARC,则声明ss.requires_arc = true.
此时主spec是ARC而JTObjectMapping是MRC,对JTObjectMapping中的文件都会添加一个-fno-objc-arc的编译宏。

15. 基础组件的版本更新

当主工程发布了版本,那么依赖的基础组件版本也固定,不能再做修改。当需要对基础组件进行修改,那么版本需要更新,版本号加一。
当某个基础组件A的版本号修改,那么依赖这个组件的所有组件BCD也都要修改版本。然后依赖BCD的组件EFG也要修改版本号,以此类推

16. 发布版本修改

发布版本修改:
基础组件,将基础组件的所有依赖修改为大版本(如“=1.1.0”格式),必须指定版本号。比如有一个组件的版本号是1.0.5,那么这个版本进行了1.0.1到1.0.5的开发,在提交版本的需要修改1.1.0版本,并执行1.1.0版本。为了,(1)方便线上版本可以指定具体的版本。 (2)方便下一个版本的开发。
壳工程中,pod file文件的修改,将业务组件制定dev分支,修改为制定版本号,必须指定版本号。

业务组件的使用:
开发时,podfile中直接dev分支获取代码,并在dev上进行开发。
等需要发布版本进行冒烟测试时,修改podfile为制定版本号。如“=1.1.0”。

pod 'Location', :git => 'git@code.xxx.com:ios/Location.git', :branch => 'dev'

基础组件的使用
在每个基础组件中,使用“~>1.0.3”进行开发,并在每次修改的时候,添加版本号。
基础组件依赖其他组件的时候,也修改为“~>1.0.3”, 那么在pod install的时候,就可以更新到最新的版本,不需要清理缓存。
发布版本的时候,在每一个组件中,指定版本号。如“=1.1.0”。保存每个发布的正式版本,都可以明确的知道所有组件的版本号。
发布版本后进行开发,那么需要修改依赖版本后“~>1.1.0”,不需要清理缓存,方便开发。
开发时,版本号的修改,仅仅针对需要修改的版本号。其他版本号保持不变。

图片资源
创建一个图片资源的pod, 使用s.resources的方式进行添加图片。那么在xib中或imageNamed:"""直接可进行访问。不需要进行bundle的控制。👍👍👍

未完待续
// END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 项目组件化、平台化是技术公司的共同目标,越来越多的技术公司推崇使用pod管理第三方库以及私有组件,一方面使项目架构...
    swu_luo阅读 21,615评论 0 39
  • 最近在学习vue.js的时候发现,vue的组件化的思想对于编写代码是一个非常有用的事情。 首先为什么需要组件化? ...
    拂晓的云阅读 7,158评论 6 23
  • iOS组件化开发1 · 什么是组件化 组件化开发2 · 组件开发必备技能 这篇文章的主要内容list1、学会使用g...
    Biharry阅读 8,869评论 2 114
  • 杨柳邀风近,江南四月春。碧波推影渡舟深,岸头羇旅人。 莺语绕花九转,烟路分江各半。停船欲系且同登,把酒话故城。
    长安旧人阅读 246评论 2 14
  • 成都的天雨蒙蒙。 昨晚和一个久不见面的朋友吃饭,初时有些拘谨,直到说些旧事,一下又打开了话匣。让我想起友人的一句话...
    九零后空巢老人阅读 144评论 0 0