组件化实践之初体验

2017-08-06  本文已影响237人  罗同学_

前因

一番调研之后,我们决定使用组件化来解决当前遇到的这些问题

组件化

1.什么是组件/模块化

简单的来讲可以包括下面这两块

对应到我们当前公司目前的项目结构可以大概分为这么几块

目标

方案

1.对相关的功能模块和业务组件进行拆分,通过framework的形式发布成内部私有库
2.通过cocoapods实现对私有库的引入和版本管理
3.通过fastlane进行持续集成

技术栈

1.静态库和动态库:共享代码的方式
2.CocoaPods:强大的iOS第三方库管理工具
3.FastLane:自动化持续集成工具集

动态库和静态库

1.静态库 Static Library

即.a文件,项目源码所对应的目标文件(.o/.obj)的打包体;配合.h文件使用,暴露.a中的方法或成员

2.动态库 Dynamic Framework

即.framework文件,是一种资源打包方式,可以说是一个bundle文件夹(包含代码文件、头文件、资源文件等)

3. Library VS Framework

总结一下就是

1F301289-415F-476F-B47E-F2E072479C1A.png

我们可以看到动态库相比较静态库而言是有很多方面的优势的,然而在iOS8之前苹果是不允许我们使用动态库的,在iOS8之后苹果推出了Embedded Framework的概念,即允许我们使用动态库,我们可以在工程模板内看到Cocoa Touch Framework。苹果之所以在iOS8上推出这个东西其原因是在iOS8时苹果推出了APP Extension (程序扩展,例如我们制作通知栏的扩展程序以及允许我们使用第三方键盘等等),这种情况下需要和我们的主APP进行部分代码共享,还有一个原因就是在iOS8时推出了Swift,由于Swift的语言特性(运行时特性尚未稳定),所以目前只支持动态库。

简单的来讲Cocoa Touch Framework有以下两个特点

所以说Cocoa Touch Framework其实是阉割版的动态库,不过这也是人之常情,毕竟苹果如果开放了真正的动态库,那还不天下大乱。

我们简单来看下使用组件化后预期的项目结构

0FCDCF7C-E25E-4DD8-83E6-AA8BF3D953E1.png

首先我们目前没有对更多的模块做拆分(后续也会拆分出来),只是把商城这个模块拆分出来(由另一个商城的团队进行开发),然后以Framework的形式加入到我们主APP当中。

如上图所示我们可以看到我们的主APP需要引入我们的商城端,我们的商城端主APP同时又需要依赖于我们抽出的内部组件;并且这三者都会依赖于一些第三方的Framework来进行开发。我们最终的目标就是把这些所有的东西融合在一个工程里面,也就是我们的主APP中。

最终的方案就是让商城内部组件第三方Framework都以Framework的形式引入到我们的主APP

567E1911-C9F9-4827-98AA-A5AC9E2E407E.png

关于如何制作一个Framework网上已有很多相关的资料,可自行Google,这里就不做相应的介绍

那么当制作好我们的Framework后,如何让另一个工程能够使用到我们的Framework,我们最先想到的方法就是把我们的Framework直接导入到工程中,可能还需要添加相应的依赖和其他的一系列配置。那么问题就来了,首先这种方案依赖和配置非常繁琐,更重要的是当我们的Framework更新时我们需要做的是删除上一个版本然后添加新版本,然后可能还需要更新一些依赖配置等等,这种版本管理方式不仅效率低下而且还可能出现不可控的风险。幸运的是在我们的iOS开发中另一种有更好的管理依赖的方式,这就是我们的Cocoapods

Cocoapods

1.什么是Cocoapods

简单的来说Cocoapods是我们 iOS 应用程序开发的一个第三方库依赖的管理工具(使用Ruby开发),通过它我们可以高效率的导入、配置以及管理所用到的第三方,如果你是一个iOSCoder相信肯定对它不会陌生

2.特点/好处

简单的来说可以有以下几个方面的好处

3.原理

我们简单的来讨论下Cocoapods的功能实现原理,在讲原理之前我们首先需要了解下我们Xcode的几个大的项目工程结构

Target:一个target对应一个目标文件,也就是一个APP;target可以相互独立,也可以依赖于另一个target
Project:project是构建一个或者多个APP所需的所有文件、资源和信息的存储库,可包含多个target,可以管理不同的target间的关系,我们默认创建的工程类型就是Project

3E252D48-D0B0-4635-85B4-6AC6EE82FD1B.png

如上图所示,我们的工程Project下对应了三个target,分别是我们的App和两个单元测试target

Workspace: workspace是最大的集合,可以包含多个Xcode Project,以及要包括的任何其他文件。可以管理多个Project间的关系(引用和依赖)

如上图所示,在这种多target的情况下可能会存在两种依赖关系
1.显式依赖:某个target直接引入了另一个target
2.隐式依赖:某个target也需要依赖于另一target,但没有直接引入

我们Cocoapods的方式就是创建一个WorkspacePods Project文件,通过Pods Project文件来管理所需要依赖的第三方的Framework,然后把我们的项目工程的Project和创建的Pods Project一起放入创建的Workspace中,最后通过这个Workspace来实现对所有工程的管理

如果你的项目使用了Cocoapods,你会发现有下面几个特点:

1.主工程没有显示依赖各个第三方库,Pods 项目最终会编译成一个名为 libPods.a (Pods_....framework)的文件,主项目只需要依赖这个.a文件即可

5859815B-9F01-47BD-B473-EE1FAAF8BD1A.png FE92E691-30A4-48A0-8503-80998F74169A.png

2.对于资源文件,CocoaPods 提供了一个名为 Pods-resources.sh 的 bash 脚本,该脚本在每次项目编译的时候都会执行,将 Pods 依赖库的各种资源文件复制到目标目录中
3.CocoaPods 还通过一个名为 Pods.xcconfig 的文件来在编译时设置所有的依赖和参数

2FA89168-DFEF-46F9-871A-16AF2DA54093.png

如果什么都不干,我们的主工程肯定是不能够引用Workspace管理的第三库的。如果打开工程的Build Settings,我们可以看到Header Search PathsLibrary Search PathsFramework Search Paths等这些字段,这些字段等于告诉了我们主工程所依赖的第三方库在哪里也就是资源路径。然而知道路径只是第一步,因为我们还没有确立他们之间的引用关系,所以我们得声明一下,告诉编译器他们之间存在着引用关系,这个字段就是Other linker flags,如下图所示

CCCA5B64-B242-438D-9DC0-EA513E807423.png

4.如何使用

如果你新建一个工程,在引入Cocoapods进行管理之后,你会发现工程目录下会多出这么一些文件,如下图所示

前.png 后.png

podfile:说明文件,说明pod需要导入和管理那些依赖库
podfile.lock:用来保存已安装的pod的依赖库的版本
pods文件夹:Pod和依赖库的工程文件夹

我们简单的来看下导入的过程,如下图所示

导入过程

首先会根据我们的podfile中的地址找到对应的第三库所在的git仓库,如果有指定tag的话会去定位到对应的tag的提交(没有取最近的一次),然后会去检索工程目录下的podspec文件,通过podspec文件来做一系列的验证,比如工程名,版本号等等,验证合法之后会根据podspec中的source_file字段找到需要下载的代码文件,然后配合对应的一些资源文件和配置文件一起下载下来,共同组成我们所需要的第三方库文件

5.制作podspec

如上文所叙,要想让我们的工程能够支持cocoapods,我们需要制作工程对应的podspec,并将其发布到cocoapods上。
如何制作一个合适的podspec这里就不做叙述,可以通过下面的一些文章来进行了解

1.CocoaPods建立自己的Podspec
2.发布CocoaPods组件碰到的坑与心得体会

Fastlane

Fastlane是用Ruby语言编写的一套自动化工具集和框架,它可以非常快速简单的搭建一个自动化发布服务,并且支持Android,iOS,MacOS。比如在我们的iOS开发过程中,经常会经历从 编译->打包上传->填写应用更新数据->等待iTunesConnect编译->选择版本发布等这一系列过程,而Fastlane则可以帮我们自动化处理这些事情。
具体的可参考:小团队的自动化发布-Fastlane带来的全自动化发布
以及 Fastlane实战(一):移动开发自动化之道

踩到的坑

由于Swift不支持.a静态库的原因,当我们的第三方库内包含.a静态库,组件库内引用了这个第三库,然后我们在引入组件库时就会pod install报错,如下

 [!] The 'Pods-LJA_Example' target has transitive dependencies that include static binaries: 
(/Users/nero/Desktop/Static_Dynamic/Componment/Example/Pods/libWeChatSDK/libWeChatSDK.a)

具体的解决方案参考这篇文章组件化-动态库实战

未来

上一篇下一篇

猜你喜欢

热点阅读