欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

Swift Package 技巧及混编兼容问题详解,

来源: 开发者 投稿于  被查看 6970 次 评论:21

Swift Package 技巧及混编兼容问题详解,


目录
  • 创建 Package
  • 使用 Package
  • 维护 Package
    • 版本控制
    • 本地调试修改
  • Package 之间的依赖
    • 混编问题
      • target 拆分
      • 引用以及和 cocoapods 的兼容问题

    创建 Package

    mkdir somePath
    cd somePath
    swift package init (--type library/executable/empty/system module)
    

    其中,type 的四种类型分别对应:

    • library: 库(默认)
    • executable: 可执行文件
    • empty: 空项目
    • system module: 系统模板项目

    一般情况下默认即可

    创建 package 之后,还可以使用 swift package generate-xcodeproj 创建一个Xcode项目来编译和调试代码

    使用 Package

    在 Xcode 菜单栏中,选择 file -> add packages

    image-20220109204812598.png

    可以指定 package 的版本规则

    维护 Package

    版本控制

    在 package 路径下,使用 git init 来创建一个仓库。之后上传至远端即可

    本地调试修改

    对于通过 cocoapods 引入的库,如果想在本地修改该库并提交的话,需要在 PodFile 里面把路径改成本地的,然后再 pod install 一下,比较麻烦

    但是修改 swift package 引入的库就很方便了,直接把 package 的文件目录拖到工程目录下即可。修改好提交到远端之后,右键 delete -> remove reference (move to trash 会删掉本地文件)

    然后 file -> packages -> update to latest,即可更新到最新的版本

    Package 之间的依赖

    在每个 swift package 的目录下都有个 Package.swift 文件。内容如下:

    // swift-tools-version:5.5
    // The swift-tools-version declares the minimum version of Swift required to build this package.
    import PackageDescription
    let package = Package(
        name: "some name",
        products: [
            // Products define the executables and libraries a package produces, and make them visible to other packages.
            .library(
                name: "some name",
                targets: ["some name"]),
        ],
        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: "some name",
                dependencies: []),
            .testTarget(
                name: "some name",
                dependencies: ["some name"]),
        ]
    )
    

    其中的字段分别表示:

    • name: 库/项目名字
    • products: 库/项目生成的东西,可以是 library 或者 executable. 同一个库/项目可以生成多个 library 或者 executable
    • dependencies: 此库/项目所依赖的库,及依赖库的 URL 和版本等信息。如果依赖本地库的话,可以添加 .package(path:"local path")
    • targets: 库/项目生成的目标

    相较于 cocoapods,swift package 还是更加方便点的,而且是苹果自家的产品。但是目前很多三方库都在 cocoapods 上,swift package 的大面积普及还需要一段时间

    混编问题

    target 拆分

    Apple 官方文档里说:

    Targets can contain Swift, Objective-C/C++, or C/C++ code, but an individual target can’t mix Swift with C-family languages. For example, a Swift package can have two targets, one that contains Objective-C, Objective-C++, and C code, and a second one that contains Swift code.

    也就是说,SPM 是支持 objc 以及 C 系代码的。但是同一个 target 里面只能有一种语言,Swift 文件不能和 objc 文件放到一个 target 里。

    如果我们想要在一个 SPM 仓库里面同时放置两种语言的代码的话,就需要将仓库拆分为两个 target:

    let package = Package(
        name: "MyModule",
        platforms: [.iOS(.v11),
                    .macOS(.v11)],
        products: [
            // Products define the executables and libraries a package produces, and make them visible to other packages.
            .library(
                name: "MyModule",
                targets: ["MyModule", "MyModule_Objc"])
        ],
        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: "MyModule_Objc",
                dependencies: [],
                publicHeadersPath: "include",
                cSettings: [.headerSearchPath(".")],
                cxxSettings: [.headerSearchPath(".")]
            ),
            .target(
                name: "MyModule",
                dependencies: ["MyModule_Objc"],
                path: "Sources/MyModule",
                swiftSettings: [.define("SPM_MODE")]
            ),
            .testTarget(
                name: "MyModuleTests",
                dependencies: ["MyModule"]),
        ]
    )
    

    其中,仓库文件结构应该为:

    - MyModule
      |
      |-README.md
      |
      |-Package.swift
      |
      |-Sources
        |
        |-MyModule
        | |
        | |- MyPackage.swift  
        |
        |-MyModule_Objc
          |
          |-Include
          | |
          | |- MyObjCClass.h
          |
          |- MyObjCClass.m
    

    Sources 文件夹拆分为两个子文件夹,分别是两个 target 的路径。Swift target 依赖 objc 的 target, objc 的 target 可以设置公开 header 的路径。

    这样,objc 以及 C/C++ 这些 C 系的文件都放在 MyModule_Objc 文件夹下,Swift 文件放到 MyModule 文件夹下,就可以在同一个 SPM 仓库下实现混编了。

    引用以及和 cocoapods 的兼容问题

    在上面的 package 设置里面可以看到一行预处理宏的定义:

    swiftSettings: [.define("SPM_MODE")]
    

    这个设置是用来标识当前库是通过 SPM 方式引入的。根据上文,SPM 模式下,当前库的 Swift 和 objc 是两个 target。因此,同一个库的 Swift 文件想要调用 objc 文件的话,必须引入 objc 的 target:

    import MyModule_Objc
    // your code...
    

    但是,我们的库一般情况下还是要支持 cocoapods 的。在 cocoapods 模式下,同一个仓库的 Swift 和 objc 文件是不需要拆分为两个 target 的,pod 是通过桥接文件等来实现两者之间相互调用的。

    那么问题就来了,当使用 cocoapods 引入当前库时,import MyModule_Objc 是肯定会报错的,因为 pod 并不会生成 MyModule_Objc

    这时候,我们在 package 文件里预先定义的宏就派上用场了:

    #if SPM_MODE
    import FoundationX_Objc
    #endif
    

    只有在 SPM 模式下,才会 import MyModule_Objc,这样就解决了与 cocoapods 的兼容问题。

    以上就是Swift Package 技巧及混编兼容问题详解的详细内容,更多关于Swift Package混编兼容的资料请关注3672js教程其它相关文章!

    您可能感兴趣的文章:
    • Swift 中的 RegexBuilder学习指南
    • Swift中的高阶函数功能作用示例详解
    • Swift中的可选项Optional解包方式实现原理
    • Swift重构自定义空等运算符 “??=” 实例
    • Swift 重构重载运算符示例解析
    • SwiftUI开发总结combine原理简单示例详解
    • 仓库模式及其在Swift 项目中的应用详解
    • swift依赖注入和依赖注入容器详解
    • Swift 中 Opaque Types学习指南

    用户评论