Objective-C製フレームワークをSwiftPMで配布する

# はじめに

この記事では、Objective-Cで書かれたフレームワークをSwift Package Manager (SwiftPM)で配布する方法を紹介します。

ObjC製フレームワークをSwiftPMで配布することは可能なのか気になったのが事の発端です。SwiftPMは*Swift*と付くだけあって、Swift専用のパッケージマネージャーなのか、ObjC製フレームワークでも使えるのかどうか実験してみました。

結論から言うと、**ObjC製フレームワークであってもSwiftPMで配布することは可能**でした。
ただし、ObjCファイルのまま(オープンソースプロジェクトのまま)配布することはできず、**xcframeworkにビルドした状態で配布する**形に落ち着きました。
(ObjCファイルのまま配布する方法をいろいろ試行錯誤しましたが、上手くいかず)

今回の実験では、僕が以前作ったObjC製フレームワーク [LoggingViewKit](https://github.com/HituziANDO/LoggingViewKit)を使用しました。SwiftPMで以下のURLを検索することでインポートできます。

# ObjC製フレームワークをSwiftPMで配布する

## 1. xcframeworkにビルドする

ObjCコードで書かれたフレームワークをxcframeworkにビルドします。
※この記事では、xcframeworkの作り方は割愛します。

作成したxcframeworkは、Frameworksディレクトリ直下に配置するものとします。
以下はパッケージGitリポジトリのディレクトリ構造になります。

```:ディレクトリ構造(一部抜粋)
/
L Frameworks/
  L LoggingViewKit.xcframework
L Sources/
  L LoggingViewKit/
L Tests/
  L LoggingViewKitTests/
L Package.swift
```

Sources、Testsディレクトリは`swift package init`してできたデフォルトのままです。(これらのディレクトリは不要かもしれないです)

## 2. Package.swiftを編集する

`swift package init`でできたPacakge.swiftを以下のように編集します。フレームワーク名やplatformsは適宜読み替えてください。

```swift:Package.swift
// swift-tools-version: 5.6
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription

let package = Package(
    name: "LoggingViewKit",
    platforms: [.iOS(.v9)],
    products: [
        .library(
            name: "LoggingViewKit",
            targets: ["LoggingViewKit"]),
    ],
    dependencies: [],
    targets: [
        .binaryTarget(
            name: "LoggingViewKit",
            path: "Frameworks/LoggingViewKit.xcframework"
        ),
    ]
)
```

xcframeworkを配布する際のポイントは`binaryTarget`を使うところです。`name`にはフレームワーク名を、`path`には上記で配置したxcframeworkのパスを設定します。

:::note info
`binaryTarget(name:path:)`はxcframeworkをパッケージのGitリポジトリに配置したときに使います。
:::

:::note info
xcframeworkをリモートサーバに配置したときは`binaryTarget(name:url:checksum:)`を使います。(下記参照)
:::

#### リモートサーバにxcframeworkを配置する場合

xcframeworkをAWS S3などのリモートサーバに配置したときは、Package.swiftのtargetsを以下のように編集します。

```swift:Package.swift (リモートサーバの場合)
// swift-tools-version: 5.6
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription

let package = Package(
    name: "LoggingViewKit",
    platforms: [.iOS(.v9)],
    products: [
        .library(
            name: "LoggingViewKit",
            targets: ["LoggingViewKit"]),
    ],
    dependencies: [],
    targets: [
        .binaryTarget(
            name: "LoggingViewKit",
            url: "https://url/to/some/remote/LoggingViewKit.xcframework.zip",
            checksum: "a94cb02cb0788c43bbd9f6a559c5d6ba40ba70701503f56aa092ebfdd2d89606"
        ),
    ]
)
```

チェックサムは`swift package compute-checksum`コマンドで算出できます。

```shell
swift package compute-checksum LoggingViewKit.xcframework.zip
```

LoggingViewKit.xcframework.zipは、上記で作成したLoggingViewKit.xcframeworkをzip圧縮したファイルです。

## 3. git pushする

最後に編集したパッケージGitリポジトリをプッシュして完了です。
お疲れ様でした。

# 参考

以下の記事を参考にしました。

https://developer.apple.com/documentation/xcode/distributing-binary-frameworks-as-swift-packages

https://qiita.com/mesummery/items/a5f754d3b6d1e107fffb

https://tech-blog.optim.co.jp/entry/2020/12/17/100000