Aug 6

多子项目的现代Xcode项目结构

Lrdcq , 2017/08/06 22:32 , 程序 , 閱讀(5556) , Via 本站原創
自从有了cocoapods这种现代的xcode项目依赖管理工具,我们就很少使用传统的xcode提供的子项目+gitsubmodule的依赖管理模式了。不过这并不是意味着子项目这种项目结构就已经用不上了,在复杂的项目关系中,我们同样可以通过子项目的形式来梳理多层项目关系,来同时避免通过cocoapods来组织项目结构带来的复杂性。

需求和设计

我们现在有两个app项目需要开发,其中一个已经开发完成,另外一个刚开始版本迭代。但是比较特殊的是,虽然是两个app,但这两个app从产品设计上较为相似,并且会随着产品设计的迭代会越来越相似越来越一致,最后有可能除了logo和应用名不同,两个app就是一摸一样的。当然,我们肯定希望代码最终是完全复用,而不是copy的,因此这里面临一些问题:

1. 这两个项目由于会越来越相似,迭代速度也很快,因此搞不好会常常涉及到到模块和界面的重构来完成代码的合并和复用,因此复用部分常常会涉及修改,因此如果把复用代码抽取为cocoapods的依赖项目,整个开发过程会非常繁琐和恶心。

2. 同时随着合并的复用代码越来越多,复用的内容可能会从基础,迭代到界面组件和模块,再迭代到业务界面和模块,直到把所有业务覆盖,并且两个app之间有一前一后,因此实际的抽象和依赖结构,因此是一个清晰的层次,这种层次通过子项目的依赖关系是最适合解决的。

所以这种场景,我们选择用子项目来解决。整个项目结构如下图:
點擊在新視窗中瀏覽此圖片
开发过程是:

1. 按从下往上迭代,准备最下面2-3层,将AppA中可以抽取的内容下层到其中。
2. AppB依赖已经准备好的基础代码进行开发。
3. 随着迭代合并AppA和B的业务逻辑并下层到第四层,同时对应该下层到下面3层的内容进行持续迭代。

通过这个过程,来保障两个app按时迭代需求的同时,进行同步的合并工作。

Xcode项目

因此,上面那张图中的每一个框就是整个workspace中的一个项目,其中最上层的两个项目当然是app项目,而下面的子项目主要是静态library和framework,其中ui和以上的项目为framework,目的是可以放置图片等静态资源。

而实际项目中的关系,是通过头文件和编译这两个侧面开看的:

header

直接在项目设置中的HEADER_SEARCH_PATHS中设置搜索的header路径即可在源码层面进行依赖层次约束,其中每一层都会依赖其下面的所有子项目,比如这是foundation的header设置:
點擊在新視窗中瀏覽此圖片

link

除了源码,我们当然要把子项目编译进去,这里就涉及到Link Binary With Libaries中link的东西了。当然,理论上我们只需要上层link下层,即app link mall,mall link foundation,foundation link library,library link base,这样普通的串起来就可以了,当时事实上我们第二层有library和ui两个子项目,如果它们都link了base的话,就会有重复link问题了。因此这种链式的link关系在同一层有多个子项目的情况下并不适用。

最后实际的link方式,是让app link了所有的东西,而下层的子项目只编译自己不link别的任何东西。

依赖管理

回到真正的依赖管理即通过cocoapods来管理第三方依赖,这个无论如何也是不能少的。不过还好,cocoapods是支持多项目的依赖设定的,而且幸运的是,这个功能pod1.2.x一直有bug,是最近出的1.3.x才修复的,因此我们用它正好。下面是一段我们实际的pod配置:
workspace 'kmall.xcworkspace'

def klm_base_pod
  pod 'AFNetworking', '~> 2.6.0'
  pod 'Mantle', '1.5.8'
  pod 'Masonry', '1.0.2'
  pod 'SDWebImage', '3.8.2'
  pod 'ReactiveCocoa', '2.5'
  pod 'UICKeyChainStore', '2.1.0'
  pod 'SVProgressHUD'
end 

target :kmall do
  xcodeproj 'kmall.xcodeproj'

  klm_base_pod

  pod 'IQKeyboardManager'
  pod 'INTUAnimationEngine'
  pod 'NYTPhotoViewer', '~> 1.2.0'

  #pod 'Flurry-iOS-SDK/FlurrySDK' #Analytics Pod

  #fix error
  pod 'OpenSSL-Universal','~>1.0.2.1'

  pod 'WebViewJavascriptBridge', '4.1.5.4'
  pod 'Raven'
end

target :base do
  xcodeproj 'base/base.xcodeproj'

   klm_base_pod
end

有几个需要注意的点:

1. 因为有多个子项目,因此每个target中必须要指定xcodeproj,否则target只会从podfile所在的根目录的最后一个项目中找,生成的xcworkspace的名字也是按照这个项目来的,这肯定不符合我们的期望。

2. pod最后只会为这个xcworkspace生成一份pod子项目,不会管你的子项目之间实际是否有依赖关系。因此,各个子项目的依赖之间不能有依赖冲突,否则pod是无法生成的。我这里通过写函数klm_base_pod的形式来把公共的的pod部分抽取出来,来尽量避免依赖配置不一致的问题。

3. 如果子项目依赖了某个第三方库,父项目也一定要依赖它,否则pod生成的pod项目给对应的项目生成的.a文件会缺少东西使得编译出错。

另外这种复杂的pod文件情况,除了抽函数,也可以考虑把各个依赖包含其版本号独立管理起来,也是不错的方法。
关键词:ios , xcode , subproject
logo