将工程从 CocoaPods 迁移到到 SwiftPM

[迁移背景故事]:
国庆前把之前一个已经发布的工程改了下配置重新上架,结果编译死活通过不了,报的错主要是Segmentation fault: 11Command CompileSwift falied with a nonzero exit code :,项目初次遇到这个问题时是在第一次打包时,当时查阅资料说是 Xcode 的 Bug 清除缓存重新编译就顺利上线了。

这次网上各种方法试了个遍都没效果,好在是个小工程不复杂,项目在做模块化的时候使用 CocoaPods 配合 framework 拆分了基础模块和公共业务。最后迫不得已将分拆的基础模块又导入回主工程下,再次编译,发现是 MJRefresh 给组件附加的 header 和 footer 在调用时没有加上可选标识的问号,修复后编译通过。

推测是因为 MJRefresh 由 Objective-C 编写而成,之前的版本可能是旧版本 Xcode 对 OC 类型进行 可选检查 没有深入到子模块,最新版本的 Xcode 新增了对 OC 对象更深入的检查却没有对子模块响应的提示,以至于我们打开 Swift 报错检查等操作并没有具体信息。

好巧不巧,这几天鼓捣了下 SwiftPM 后,这个为上架被迫临时还原的项目正好可以拿来试试 SwiftPM 的成色如何。本文记录了该工程从 CocoaPods 迁移到 SwiftPM 过程中遇到的问题以及解决办法。

[阅读难度:简单]

以笔者的小工程为例,CocoaPods 导入的三方库大约十来个,包含了Swift与OC两种类型的库,这些库对 SwiftPM(Swift Package Mananger)的适配状态也不尽相同。迁移的过程从最简单的情况开始一个一个解决。


Swift 类型的库

Swift 类型的库属于最简单的情况,直接用 SwiftPM 引入即可,诸如 AlamofireKingfisherSnapKit 等库。对 Xcode 中如何使用SwiftPM 还不了解的同学可以参考官方文档或者 上一篇文章


Objective-C 类型的库

SwiftPM 本是专门针对 Swift 诞生的,由于历史原因,很多库是由OC编写而成,如何引入OC的包?可以分为两种情况:

1. 支持 SwiftPM 的库

这一类跟Swift的库一样直接导入就可以了,这里可以借鉴 MBProgressHUD 库的官方实现,在其 Package.swift 文件中配置如下:

import PackageDescription

let package = Package(
    name: "MBProgressHUD",
    products: [
        .library(name: "MBProgressHUD", targets: ["MBProgressHUD"])
    ],
    targets: [
        .target(
            name: "MBProgressHUD",
            dependencies: [],
            path: ".",
            exclude: ["Demo"],
            sources: ["MBProgressHUD.h", "MBProgressHUD.m"],
            publicHeadersPath: "include"
        )
    ]
)

大体和 Swift 库一致,特别注意 sources 这里,库的 OC 文件都列在了这里,publicHeadersPath 则是需要公开的头文件目录,自定义OC库想了解详细设置可查看官方文档或参考 MBProgressHUD 的实现。

但这种方式有个问题,对于MBProgressHUD这样个位数文件的OC库实行起来倒是没什么影响,如果是 MJRefresh 这样几十个文件还附带资源文件怎么办?可能这也是MJRefresh到目前为止依旧没有适配SwiftPM的原因之一。(在最新的 Swift5.3 中已经支持导入资源文件,SE-0271

2. 未支持 SwiftPM 的 OC 库

最直接的方法是自己创建一个 Swift Package 的中间库,自行配置Package.swift与各种文件。就像上面说的,这种方法耗费太多的时间,随着库的发展,三方库大改,中间库的配置就要大改,想到这里肠胃一阵蠕动, Pass。

那就曲线救国,在 Swift 为主的工程中,引入的 OC 库都比较少,且都是经得起时间考验的精品库,没有适配 SwiftPM 的就更少了。可以将这类 OC 库打包成 Framework 直接丢进项目中即可。这里笔者借助了 CocoaPods 的一个插件 cocoapods-binary,因为CocoaPods在安装三方库时会将其所以来的库一并安装,省去不少麻烦。具体过程如下:

1. 安装 cocoapods-binary:

$ gem install cocoapods-binary

2. 新建个空工程。
3. 创建Pod文件(以MJRefresh为例):

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

platform :ios, '13.0'       // 这里改为指定系统版本
inhibit_all_warnings!
use_frameworks!             // 动态库
plugin 'cocoapods-binary'    // 插件  
all_binary!                 // 插件应用于全部库

abstract_target 'BASE_POD' do

    pod 'MJRefresh'

    target 'StaticLibDemo' do 
        project 'StaticLibDemo.xcodeproj'
        workspace 'StaticLibDemo.xcworkspace'
    end
end

4.在当前工程下调用 pod install 命令
**5.生成的库在生成的 Pods 文件下,路径为: **

/Pod/_Prebuild/GeneratedFrameworks/

6.将其中生成的库复制到我们的项目中(如:MJRefresh.framework,仅仅是framework,别的不要,否则发布报错)

这里生成的库会有 x86_32 架构,在发布时会报 IPA progress fail,需要将此架构从库中移除。

具体方法:

  1. 命令面板跳转到framework: $ cd /XXX/XXXX/包名.framework
  2. 查看信息: $ lipo -info 包名,这里会显示出 i386、x86_64
  3. 移除:
    $ lipo -remove i386 包名 -o 包名
    $ lipo -remove x86_64 包名 -o 包名
  4. 再次查看信息确认移除成功后即可将包导入工程。

7.因为我们生成的是动态库,所以别忘了在 General 中将导入的动态库设置为“embed & sign”

至此,我们就可以正常替换之前在 CocoaPods 引入的 MJRefresh 了,这只是权宜之计,有更好的办法的小伙伴欢迎补充 [猛男微笑.gif]。


遇到的问题

用 SwiftPM 导入库后,编译报错 not found 'xxxxx'

建议备份一下工程,这类错误的原因不一定相同。

笔者解决方法:通过删除 Build setting 下 Other Linking 的所有配置可编译正常

RxSwift 的导入问题

因为工程以来的基础模块应用到了 RxSwift 所以基础模块的SwiftPackage依赖于RxSwift,然而这个库目前(20201015)对SwiftPM的支持是有问题的(相关链接)。而这导致使用 RxSwift 给出的配置来配置项目时,Xcode无法识别,也无法在

按照官方方法配置子模块的 Package.swift:

import PackageDescription

let package = Package(
    name: "RJSuger",
    platforms: [.iOS(.v13)],
    products: [
        .library(
            name: "RJSuger",
            targets: ["RJSuger"]),
        .library(
            name: "RJBaseWidgets",
            targets: ["RJBaseWidgets"]),
    ],
    dependencies: [
        .package(url: "https://github.com/SnapKit/SnapKit.git", .upToNextMajor(from: "5.0.1")),
        .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("6.0.0-rc.1"))
    ],
    targets: [
        .target(
            name: "RJSuger",
            dependencies:[],
            path: "Sources/RJSuger"),
        .target(
            name: "RJBaseWidgets",
            dependencies: ["RJSuger","SnapKit","RxSwift", "RxCocoa"],
            path: "Sources/RJBaseWidgets"),
        .testTarget(
            name: "RJSugerTests",
            dependencies: ["RJSuger"]),
    ],
    swiftLanguageVersions: [.v5]
)

这样的配置对于笔者的模块,Xcode 识别不出来,报出来的错误指向了 最后添加的依赖项 RxCocoa,参考 RxSwift的文件结构是RxSwift下有 RxSwift、RxCocoa、RxRelay等模块,尝试性的将对应的 Targets 依赖变更为:

//····
  dependencies: ["RJSuger","SnapKit",.product(name: "RxSwift", package: "RxSwift"),.product(name: "RxCocoa", package: "RxSwift")],
//···

编译通过。


总结

从 2018 年推出 SwiftPM 到现在,SwiftPM 已经可以应付大部分库管理的场景,对自定义模块的支持也不错,不出问题的时候使用起来真是清爽自然。而当遇到问题时,网络上资料可能很少,也可能直接就是三方库或 SwiftPM 本身的问题不得不另寻他法。最最最重要的问题是加载库的速度和机制,稍微大点的库本身速度就慢,稍微变动下工程结构,SwiftPM 就莫名所有库重新加载一次,导致大量时间浪费在这上面,而SwiftPM 的 checkout 下来的库对存在 DriveData/工程文件/SourcePackages/checkout下,对于老夫这种喜欢清理 DriveData 的人不得不备份一份在其抽风时替换。

所以个人建议:SwiftPM 建议了解下,实际应用到项目,胆子小的:

再等一年又何妨

Thanks for reading :)

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