王景春 發表於 2023-3-13 16:12:02

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

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>创建 Package</li><li>使用 Package</li><li>维护 Package</li><ul class="second_class_ul"><li>版本控制</li><li>本地调试修改</li></ul><li>Package 之间的依赖</li><ul class="second_class_ul"></ul><li>混编问题</li><ul class="second_class_ul"><li>target 拆分</li><li>引用以及和 cocoapods 的兼容问题</li></ul></ul></div><p class="maodian"></p><h2>创建 Package</h2>
<div class="cros igoods"><div class="goodsin" data-img="https://img14.360buyimg.com/pop/jfs/t3229/39/2924280771/392779/d242f52a/57e8d68eN57129bbf.jpg" data-name="精通Swift设计模式(图灵出品)" data-owner="京东自营" data-price="77.4" data-tgid="38" data-url="https://union-click.jd.com/jdc?e=&amp;p=JF8BAMoJK1olXwUFU1xdCE4TBl8IG1MTWgIAVm4ZVxNJXF9RXh5UHw0cSgYYXBcIWDoXSQVJQwYCXFhaDEkVHDZNRwYlJHVXDCgDcCNydxULElsSBBh_SiEmTkcbM2gNHF4dXwMBZF5eDkwXAmoIK2sVXDZQOobrvpOysnPcsdTA1ZEyVW5dD00eBGcMG1wdVQAGZF5VDHtUVypcWBhdbTYyV25tOEsnAF9WdVpGWwQCVwpaZhZABDRLTFoWMwYLXVpVD00RC18KGloXXzYy"></div></div>
<div class="jb51code"><pre class="brush:cpp;">mkdir somePath
cd somePath
swift package init (--type library/executable/empty/system module)
</pre></div>
<p>其中,type 的四种类型分别对应:</p>
<ul><li>library: 库(默认)</li><li>executable: 可执行文件</li><li>empty: 空项目</li><li>system module: 系统模板项目</li></ul>
<p>一般情况下默认即可</p>
<p>创建 package 之后,还可以使用 <code>swift package generate-xcodeproj</code> 创建一个Xcode项目来编译和调试代码</p>
<p class="maodian"></p><h2>使用 Package</h2>
<p>在 Xcode 菜单栏中,选择 file -&gt; add packages</p>
<p style="text-align:center"><img alt="image-20220109204812598.png" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/568e83b97d8e4e9bbd8c54840e682060~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp?" /></p>
<p>可以指定 package 的版本规则</p>
<p class="maodian"></p><h2>维护 Package</h2>
<p class="maodian"></p><h3>版本控制</h3>
<p>在 package 路径下,使用 <code>git init</code> 来创建一个仓库。之后上传至远端即可</p>
<p class="maodian"></p><h3>本地调试修改</h3>
<p>对于通过 cocoapods 引入的库,如果想在本地修改该库并提交的话,需要在 PodFile 里面把路径改成本地的,然后再 <code>pod install</code> 一下,比较麻烦</p>
<p>但是修改 swift package 引入的库就很方便了,直接把 package 的文件目录拖到工程目录下即可。修改好提交到远端之后,右键 delete -&gt; remove reference (move to trash 会删掉本地文件)</p>
<p>然后 file -&gt; packages -&gt; update to latest,即可更新到最新的版本</p>
<p class="maodian"></p><h2>Package 之间的依赖</h2>
<p>在每个 swift package 的目录下都有个 <code>Package.swift</code> 文件。内容如下:</p>
<div class="jb51code"><pre class="brush:cpp;">// 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"]),
    ]
)
</pre></div>
<p>其中的字段分别表示:</p>
<ul><li>name: 库/项目名字</li><li>products: 库/项目生成的东西,可以是 library 或者 executable. 同一个库/项目可以生成多个 library 或者 executable</li><li>dependencies: 此库/项目所依赖的库,及依赖库的 URL 和版本等信息。如果依赖本地库的话,可以添加 <code>.package(path:&quot;local path&quot;)</code></li><li>targets: 库/项目生成的目标</li></ul>
<blockquote><p>相较于 cocoapods,swift package 还是更加方便点的,而且是苹果自家的产品。但是目前很多三方库都在 cocoapods 上,swift package 的大面积普及还需要一段时间</p></blockquote>
<p class="maodian"></p><h2>混编问题</h2>
<p class="maodian"></p><h3>target 拆分</h3>
<p>Apple 官方文档里说:</p>
<blockquote><p>Targets can contain Swift, Objective-C/C++, or C/C++ code, but an individual target can&rsquo;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.</p></blockquote>
<p>也就是说,SPM 是支持 objc 以及 C 系代码的。但是同一个 target 里面只能有一种语言,Swift 文件不能和 objc 文件放到一个 target 里。</p>
<p>如果我们想要在一个 SPM 仓库里面同时放置两种语言的代码的话,就需要将仓库拆分为两个 target:</p>
<div class="jb51code"><pre class="brush:cpp;">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"]),
    ]
)
</pre></div>
<p>其中,仓库文件结构应该为:</p>
<div class="jb51code"><pre class="brush:plain;">- MyModule
|
|-README.md
|
|-Package.swift
|
|-Sources
    |
    |-MyModule
    | |
    | |- MyPackage.swift
    |
    |-MyModule_Objc
      |
      |-Include
      | |
      | |- MyObjCClass.h
      |
      |- MyObjCClass.m
</pre></div>
<p>Sources 文件夹拆分为两个子文件夹,分别是两个 target 的路径。Swift target 依赖 objc 的 target, objc 的 target 可以设置公开 header 的路径。</p>
<p>这样,objc 以及 C/C++ 这些 C 系的文件都放在 <code>MyModule_Objc</code> 文件夹下,Swift 文件放到 <code>MyModule</code> 文件夹下,就可以在同一个 SPM 仓库下实现混编了。</p>
<p class="maodian"></p><h3>引用以及和 cocoapods 的兼容问题</h3>
<p>在上面的 package 设置里面可以看到一行预处理宏的定义:</p>
<div class="jb51code"><pre class="brush:cpp;">swiftSettings: [.define("SPM_MODE")]
</pre></div>
<p>这个设置是用来标识当前库是通过 SPM 方式引入的。根据上文,SPM 模式下,当前库的 Swift 和 objc 是两个 target。因此,同一个库的 Swift 文件想要调用 objc 文件的话,必须引入 objc 的 target:</p>
<div class="jb51code"><pre class="brush:cpp;">import MyModule_Objc
// your code...
</pre></div>
<p>但是,我们的库一般情况下还是要支持 cocoapods 的。在 cocoapods 模式下,同一个仓库的 Swift 和 objc 文件是不需要拆分为两个 target 的,pod 是通过桥接文件等来实现两者之间相互调用的。</p>
<p>那么问题就来了,当使用 cocoapods 引入当前库时,<code>import MyModule_Objc</code> 是肯定会报错的,因为 pod 并不会生成 <code>MyModule_Objc</code>。</p>
<p>这时候,我们在 package 文件里预先定义的宏就派上用场了:</p>
<div class="jb51code"><pre class="brush:cpp;">#if SPM_MODE
import FoundationX_Objc
#endif
</pre></div>
<p>只有在 SPM 模式下,才会 <code>import MyModule_Objc</code>,这样就解决了与 cocoapods 的兼容问题。</p>
<p>以上就是Swift Package 技巧及混编兼容问题详解的详细内容,更多关于Swift Package混编兼容的资料请关注琼殿技术社区其它相关文章!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>Swift 中的 RegexBuilder学习指南</li><li>Swift中的高阶函数功能作用示例详解</li><li>Swift中的可选项Optional解包方式实现原理</li><li>Swift重构自定义空等运算符&nbsp;“??=”&nbsp;实例</li><li>Swift 重构重载运算符示例解析</li><li>SwiftUI开发总结combine原理简单示例详解</li><li>仓库模式及其在Swift 项目中的应用详解</li><li>swift依赖注入和依赖注入容器详解</li><li>Swift 中 Opaque Types学习指南</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Swift Package 技巧及混编兼容问题详解