Swift: SPM包管理器

SPM:Swift Package Manager(swift包管理器),管理Swift代码分发的工具,用于处理模块代码的下载、编译和依赖关系。类似CocoaPods,不过比CocoaPods更简洁,代码的侵入性更小,也不需要额外安装工具。

SPM依赖安装

Xcode自带SPM,终端上可查看SPM版本:

$ swift package --version
Swift Package Manager - Swift 5.3.0

新建项目SPMTest,添加SPM依赖,File -> Swift Package -> Add Package Dependency...

add_spm.png

或者点击到 PROJECT -> Swift Packages 点击加号添加依赖

add_spm1.png

输入需要添加的第三方库的链接:

add_lab.png

点击next等待验证成功后直,根据自己的实际需要选择版本,如下图:

package_version.png

有三个选项:

  • Version: 对应库的Release版本, 这里可选择版本规则,它会自动下载这个规则内最新的版本

    • Up to Next Major: 指定一个主要版本的范围,例如:5.4.2~6.0.0
    • Up to Next Minor: 指定一个小版本范围,例如:5.4.2~5.5.0
    • Range: 指定一个版本范围, 例如:5.4.1~5.5.1
    • Exact: 指定一个确切的版本,例如:5.4.1
  • Branch: 直接下载某个分支的代码

  • Commit: 根据某一次提交记录的 Id下载

添加完成之后,项目中会出现Swift Package Dependencies这样一个目录:

add_dependencies.png

这样就可以在项目中直接使用这个第三方依赖库了。

如果你要更新SPM中的依赖,选择 File -> Swift Packages -> Update to Latest Package Versions 即可。

如果想要修改某个第三方库的版本策略,可以双击第三方库即可出现修改面板进行相应的修改。

创建本地Swift Package库

我们新建一个Swift Package,打开我们上面用到的项目SPMTest后选择File->New->Swift package...,把这个包命名为ZZPackage,并添加到现有的项目中。

create_spm.png

新建完成后可以看到在项目工程中包含了ZZPackage这个Package,

show_zzjpackage.png

如何引入ZZPackage到工程中并使用其中的功能模块呢?

Targets ->General -> Frameworks..部分,点击+号,添加ZZPackage

link_package.png

到这里我们就把ZZPackage引入到我们的项目中了。在ZZPackage下的 Sources/ZZPackage 目录下新建ExView.swift,然后在工程中使用到这个文件中的方法:

import SwiftUI

extension View {
  //module对外的访问权限设为public
   public func printLog(_ value:Any) -> some View {
        #if DEBUG
        print(value)
        #endif
        return self
    }
    
}

直接编译报错了:

comp_error.png

可以看到要求iOS13及以上,因为我们加入的是swiftUI代码,所以需要在Package.swift中添加platforms: [.iOS(.v13)],或在扩展代码上面添加@available(iOS 13.0, *)

这两种方式编译都可以成功!

运行看下结果吧:

run_result.png

发布你的 Swift Package

找到SPMTest文件夹下的ZZPackage文件夹,上传库到云端(github, gitee 或者其他托管服务器)。
然后设置Tag版本号就可以了。删除本地Package,就可以通过仓库地址加载远程Package了。

发布后远程加载

打tag发布到GitHub后,File > Add Packages 在搜索中输入地址,这个时候可能一直搜索加载报错。这并不是你的包有问题。换一种方式加载,创建本地Package关联项目,然后添加远程依赖项目,这样加载比较快了

  • File > New > Package
  • 选择把新建的 Swift Package 添加已有的项目中去,Package保存为Library
local_depend.png
  • 此时的项目结构如下:
project_struct.png
  • Targets ->General -> Frameworks..部分,点击+号,添加Library 然后编译一下
  • 添加其他远程依赖库,例如添加STNavigationController
dep_others.png
  • 它会自动加载依赖,加载成功后就可以在项目中使用依赖的库了
dep_results.png

SPM文件及配置

dep_config.png
  • Source文件夹: 第三方库源码位置路径文件
  • Package.swift: SPM配置文件
    我们来看下Package.swift这个文件:
// swift-tools-version:5.3

import PackageDescription

let package = Package(
    name: "ZZPackage",
    platforms: [.iOS(.v13)],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "ZZPackage",
            targets: ["ZZPackage"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "ZZPackage",
            dependencies: []),
        .testTarget(
            name: "ZZPackageTests",
            dependencies: ["ZZPackage"]),
    ]
)

第一行始终是Swift Tools的版本,这里是swift5.3版本。这行注释说明了构建swift package所需最低的swift版本号。然后我们导入了PackageDescription,这个库提供了我们需要配置swift package所需的API。

  • name: 包的项目名称
  • platforms: 支持的平台及对应平台的最低版本
  • targets: 包含多个target的集合,我们指定target的名字为ZZPackage,xcode会自动把Sources/ZZPackage目录下的所有文件添加到package中。如果你想再新建一个target, 需要在Sources/目录下新建一个文件夹,然后再targets数组中添加新的target。
    .target是PackageDescription.Target实例类,参数说明:
    • name: target名字
    • dependencies: target的依赖,主要指定Package添加的依赖module的名字
    • path: target的路径,如果自定义文件夹需要设置此参数
    • exclude: target path中不希望被包含的path
    • sources: 资源文件路径
    • publicHeadersPath: 公共header文件路径
  • products: 对外公开导出target产物,使得其他target能够使用它们。如果不写会编译报错

    • .library(
      name: "ZZPackage",
      type: .static,
      targets: ["ZZPackage"]) : 可指定静态库或动态库,默认静态库
  • dependencies: 添加包所依赖的其他第三方package包的集合

  • swiftLanguageVersions: 支持的swift版本

resources 添加资源文件

.target下,我们可以添加资源文件字段resources,例如添加图片资源自定义文件夹imgs

resources_imgs.png

如果资源或路径添加不对,编译报错!

对于resources属性,有两个静态方法: process()copy()

  • copy会直接拷贝,保存目录结构,可直接copy文件夹
  • process是推荐的方式,它所配置的文件会根据具体使用的平台和内置规则进行适当的优化,但好像只能针对单个文件,而不能处理整个文件夹下的资源
    //调用举例
    public func bgImg() -> some View {

        let path = Bundle.module.path(forResource: "imgs/wechat@2x.png", ofType: nil)
        
        guard let uiimage = UIImage.init(contentsOfFile:path!) else {
            fatalError("image load path error: \(path as Any)")
        }
        let img = Image(uiImage: uiimage)
        return self
            .background(img)
        
    }

.target下,我们可以添加资源文件字段resources,使用默认文件夹Resources,推荐使用这种:

resources.png

这种方式可以直接在process中指定文件夹Resources,使用的时候也无需引用该路径。swift在编译的时候不会添加Resources路径。

    public func bgImgUrl() -> some View {
        
        let path = Bundle.module.url(forResource: "wechat@2x", withExtension: "png")
        guard let data = try? Data(contentsOf: path!),
              let uiimage = UIImage.init(data: data)
        else {
            fatalError("image load path error: \(path as Any)")
        }
        let img = Image(uiImage: uiimage)
        return self
            .background(img)
        
    }

这里也有一个第三方使用resources的案例:

NutritionFacts.png

Assets.xcassets中添加的图片资源不需要使用路径方式,使用方式如下:

Image("imageName", bundle: .module)

SPM不支持混合语言开发,在同一个target中无法使用多语言,否则编译报错。

如何实现混合

可参考这个文章

这里是个Swift和OC混编的示例,每种语言一个target,单个target内不可使用多语言:

spm_objc.png

对外导出的.h头文件,默认放在include文件中。如果要自定义导出文件需要设置publicHeadersPath导出的公共头文件夹路径。这里我们把原来的include文件名改为header,如下所示:

custom_path.png

然后把这个OC的包添加依赖到需要使用的Swift的包里,就可以正常使用了!!

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

推荐阅读更多精彩内容