XCode中引入workspace管理平台型应用

背景介绍

移动互联网已经从1.0进化到2.0,移动APP也从单体应用进化到了平台型客户端。稍微上点规模的APP,已经不再是一个简单的MVC就能清晰表达的小儿科了。
常见场景:业务部门A有APP1,业务部门B有APP2......,为了模块复用,公司可能还会成立一个平台部,给各个业务部门提供公用组件。
做过VS开发都知道,一般上来是workspace,然后project,然后dll;对应到XCode,这一套也是同样可以的,只是目前还没流行而已
这是Swift版本的,基于Carthage库管理的。 Object-C的话,由于CocoaPods的原因,侵入性太强,会自动生成应用同名的workspace,所以不需要手动创建workspace文件,不过目录结构要注意一下。

基本结构

分为workspace -》project -》target -》File 四级

  • 当前,workspace基本不用,出现的也基本是CocoaPods自动生成的,没起什么作用

  • target:XCTestCase就是project中的一个target。这个还不是很好用,也不是很普遍。

  • 目前常用的就project -》File两级。用文件夹区分不同模块。当前的APP,稍微分一下就是三层,界面、逻辑、数据。界面层适合用页面分模块;逻辑层适合按照业务逻辑分模块;数据层适合按照数据格式分模块。不同的分类标准,都通过文件夹的名字来区分,由于标准众多,会显得比较混乱。

  • 引入framework之后,可以改善这种状况。不过,framework没有嵌套的概念,只有引用依赖的概念。在逻辑上可能有好几层,但物理上其实只有两层:主工程和framework内部。主工程可以调用framework,但是framework不能主动调用主工程。framework1想要调用framework2,实际上是要主工程介入帮忙的,只要将framework1和framework2都导入主工程,两个framework就可以互相调用了,两者完全平等。

程序架构

XCode中引入workspace管理平台型应用_第1张图片
程序框架.jpg
  • Platform:主程序,基本上是一个容器,提供各业务入口

  • Business.framework:业务模块,强调独立性,强调隔离

  • Service.framewrok:服务,直接供业务模块调用

  • Component.framework:公共组件,或者微服务,供其他模块调用。在现实中,严格的微服务是比较难做到的,所以可以适当放宽要求,尽量减少对其他模块的引用和调用。当然,也可以只到服务这一层概念,所以这里用了component组件概念,并没有用微服务的概念。

  • Alamofire.framework:第三方库,网络方面的

  • 这个是程序的逻辑架构,当然实际的比这个更复杂,层级可能更多。程序的物理结构就是简单的两层:主程序Platform和各种framework。

操作步骤

  1. 创建文件夹Platform,名字也可以带上版本号,比如Platform1.0.0

  2. 创建workspace文件,Platform.xcworkspace

  3. 创建主工程Platform.xcodeproj,工程类型选“Single View Application”,语言选"Swift",保持"Include Unit Test"勾上不动,保存时选择加入workspace。Target的概念目前就用默认的单元测试,做不做,看情况吧,位置先留着。

  4. 创建 Business.framework,类型选framework,不要选static library,用动态库,不要静态库,除非有十二分的理由。“Add to”和“Group”都选择workspace

  5. 用同样的方式创建Service.framewrok:Component.framework:等工程

  6. 版本号都设为8.0;framework从iOS8才开始用。并且主工程的版本号不能低于framework的版本号,否则会崩溃。大家都设成8就没事了。以后主工程可以升,framework保持不变。一般framework都是些跟界面无关的逻辑或者数据,不需要经常升版本的。实在有必要,才设为8以上的版本。

  7. 创建Carthage文件Carthage Cartfile,写上第三方库的内容(github "Alamofire/Alamofire" ~> 3.4)(当然可以有更多第三方库),保存在和workspace同一目录下

  8. 打开终端工具,定位到工程文件夹下,运行命令carthage update,下载并编译第三方库。至此,基本的准备工作算是完成了。这些步骤也只是建议,乱了也没关系,总是有办法调整过来的,只是有时候会麻烦一点。

XCode中引入workspace管理平台型应用_第2张图片
文件结构.jpg
XCode中引入workspace管理平台型应用_第3张图片
工程结构.jpg

编码实现

  • 入口函数的名字为framework的名字加上Manager后缀,其整个模块的管理作用。

  • 采用类,并且用单例,表示是一个容器,概念比较大,并且只有一个入口,比较好理解。

  • 同时也可以提供一个全局函数的入口,方便调用,内部照样调用这个单例入口,还是统一的。这种用法在第三方库Alamofire中可以见到。

  • 如果要考虑给Object-C的工程用,应该继承自NSObject;如果没有这种打算,就不需要有基类了,这里是纯Swift的思路,不继承。

  • 这个类只起到管理和入口的作用,而且是单例,所以不希望被继承,加上final关键字

  • 入口函数统一为func invokeWithUrl(url: String) -> (),仅仅用URL来拉起相关模块

  • 具体的函数调用,就像页面级的定位一样,参数回传目前感觉还是不适合用url的方式。用Block或者delegate感觉更适合一点。比如网络响应有时候会有一大堆数据,很容易超过url字符串长度限制

/ 单例入口
public final class BusinessManager {
    public static let sharedInstance = BusinessManager()
    
    public func invokeWithUrl(url: String) {
        print(url)
        Service1.invokeWithUrl("Service1://function?from=Business¶m1=value1¶m2=value2")
        Service2.invokeWithUrl("Service2://function?from=Business¶m1=value1¶m2=value2")
        Component.invokeWithUrl("Component://function?from=Business¶m1=value1¶m2=value2")
    }
}

// 全局函数入口
public func invokeWithUrl(url: String) {
    BusinessManager.sharedInstance.invokeWithUrl(url)
}

调用和依赖

主程序Platform调用Business.framework

  • 引入模块,就像系统的framework一样

  • 在ViewDidLoad函数中直接引用public标记的属性和函数

import UIKit
import Business

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
            BusinessManager.sharedInstance.invokeWithUrl("Business://function?from=Platform¶m1=value1¶m2=value2")
    }
}
  1. 这里竟然不需要在“General”标签的“Linked Frameworks and Libraries”加入Business.framework就能在Platform里直接使用,超出预期。“什么都不用做,import一下,就直接使用”,竟然这么简单。估计是同属于一个workspace的原因吧
  1. Business.framework做了修改,直接运行Platform,修改没有显现出来。这种情况下,工程需要切换到Business.framework编译一下,再切换到Platform,运行,修改就起效果了。 === 如果有修改,在主工程或者相应的framework工程clean一下,那么就会提示重新编译
  1. 在Business.framework中加断点,直接运行Platform,断点能正确停住,就像在同一工程中一样,修改也能及时起效果。=== 这里,由于断点原因,重新编译,修改效果能够立即显现。
  1. 在模拟器中调试运行,也不涉及第三方库,“什么都不用做,import一下,就直接使用”。真机调试,或者涉及第三方库,需要做相应的调整。

framework之间相互调用

  • 由于workspace的便利,从framework中调用其他framework与主程序中调用完全一样。“什么都不用做,import一下,就直接使用”。

  • 用模拟器调试,打断点,能够正常跟踪。

  • framework之间的具体相互依赖关系不需要关心,这些信息在workspace中都有

主程序或者framework调用第三方库

  • 这里调用的第三方库是Alamofire,用Carthage进行管理

  • import Alamofire这句话一加,就会出现"No such module Alamofire",意思是找不到模块文件,编译不能通过。

  • 在"Build Settings" ->"Search Paths" -> "Framework Search Paths"添加搜索目录"$(PROJECT_DIR)/../Carthage/Build/iOS/"。这样就告诉了XCode第三方framework的位置,编译通过。

  • /../是上一级目录的意思。以前workspace和project都是同一级目录,不需要加这个。但是,现在引入workspace概念,workspace在物理文件夹上也比各个工程高一级别。

  • 在编译和链接方面,主工程和framework也是平等的。谁要用,谁的"Build Settings"就指定一下,不然编译不过。如果有好几个工程用到,那么这几个工程都要分别指定路径。

真机调试

  • 模拟器调试,什么也不用做,但是真机调试,会出现运行时错误“dyld: Library not loaded ...... Reason: image not found”

  • 原因是framework没有加载,不管是Carthage管理的第三方库framework还是自己写的framework,两者都一样,完全平等,手机根本就区分不了。看到的现象是崩溃,或者应用干脆就起不来。

  • 由于主应用负责调起其他framework,所有framework在主应用看来都是平等的,所以加载framework的事交给主应用统一做,不要关心framework之间复杂的调用关系。

  • 在主程序的“General”-》“Enbedded Bianries”将所有用到的framework都拖进去,包括第三方库和自己写的framework。注意这里不要copy,只要引用就可以了。否则今后更新会很麻烦。

XCode中引入workspace管理平台型应用_第4张图片
不要copy framework.jpg
  • “Linked Frameworks and Libraries”标签下自动会出现相应的framework,不用去管它。

  • 如果自己的framework或者第三方库中用到了系统的framework,直接在“Linked Frameworks and Libraries”标签下加就可以了。在工程目录上也可以将系统的framework和其他的framework都区分开来

  • 在主工程目录中,会出现添加过的framework,这里只是引用,并不是实际的位置。这里可以把它们都放在一个虚拟文件夹中,比如叫Framework什么的,看上去清爽一点。

  • 这里需要添加的framework都是真机版的framework。模拟器版本的默认就能用,不需要添加。

framework的真机版本和模拟器版本

  • framework分模拟器和真机两个版本,网上有将两者合二为一的方法。Xcode 6制作动态及静态Framework

  • 可以通过终端命令“lipo -create xxxx/tgfmwk xxxxx/tgfmwk -output tgfmwknew”将两个版本合并。framework的合并

  • 目前试了一下,XCode中执行脚本合并比较慢,用终端命令比较好。

  • 一般也不需要合并,将两个版本的framework都提供,命名区分清楚,让使用者自由选择。

  • Carthage编译出来的应该是两者合并后的版本

例子代码

Platform1.0

你可能感兴趣的:(XCode中引入workspace管理平台型应用)