SPM対応していないライブラリをSPM Package内で利用する

サンプルプロジェクト

https://github.com/kntkymt/UseNonSPMFrameworkInSPMSample

# どんな状況？

`SampleView` と言う`target`で `Hoge` と言うライブラリを使いたい。しかし、`Hoge`はSPM対応していない。

```swift:Package.swift
// swift-tools-version: 5.6

import PackageDescription

let package = Package(
    name: "Package",
    defaultLocalization: "en",
    platforms: [.iOS(.v15)],
    products: [
        .library(
            name: "SampleView",
            targets: ["SampleView"]),
    ],
    dependencies: [
        // SPM対応していない！
        // .package(url: "XXXXX", from: "1.0.0"),
    ],
    targets: [
        .target(
            name: "SampleView",
            dependencies: [
                // dependenciesに導入できないから使えない！！
                // .product(name: "Hoge", package: "Hoge", condition: .when(platforms: [.iOS])),
             ]
        ),
    ]
)

```

# 解決策： Carthageで.xcframeworkビルドする

`Carthage`は`.xcframework`形式でライブラリをビルドすることができます。
**`.xcframework`形式は`Package.swift`の`.binaryTarget`を利用することで`target`として定義できる**ため、他targetから利用することができます。

<details>
<summary>Carthageをインストールする方法</summary>

## Homebrewのインストール

CarthageをインストールするにはHomebrewが必要です。以下でインストールできます。

```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

## Carthageのインストール

```bash
brew install carthage
```

</details>

## Carthageによるビルド
まず、`Package` 以下に`cartfile`を作成し、導入したいライブラリを記述します。

```bash
cd path/to/Package
touch Cartfile
```

```:Cartfile
github "XXX/ライブラリ名"
```

そして、`--use-xcframeworks` オプションをつけてビルドします。

```bash
carthage bootstrap --platform iOS --cache-builds --use-xcframeworks
```

これにより、`Package/Carthage/Build`以下に`ライブラリ名.xcframework`が作成されます。

## Package.swiftでの利用

`Package.swift`で`.binaryTarget`を用いて先ほど作成した`ライブラリ名.xcframework`を参照します。

```swift:Package.swift
// swift-tools-version: 5.6

import PackageDescription

let package = Package(
    name: "Package",
    defaultLocalization: "en",
    platforms: [.iOS(.v15)],
    products: [
        .library(
            name: "SampleView",
            targets: ["SampleView"]),
    ],
    dependencies: [
    ],
    targets: [
        .target(
            name: "SampleView",
            dependencies: ["ライブラリ名"]),// binaryTargetで定義した物を利用

        // carthageで作成した.xcframeworkを参照
        .binaryTarget(
            name: "ライブラリ名",
            path: "./Carthage/Build/ライブラリ名.xcframework")
    ]
)

```

以上でSPM対応していないライブラリをSPM Package内で利用することができました。

## Carthageにも対応していなかったら？

多少手間ですが、CocoaPodsでソースをDLし、手元で直接`xcodebuild`すれば同様に`.xcframework`を生成可能です。

ここらへんの記事が参考になるはずです。

https://blog.kishikawakatsumi.com/entry/2019/06/17/090724

# まとめ

SPMに対応していないライブラリが存在しているのが原因で、multimodule by SPM の利用ができない方も多いのではないでしょうか？そんな方々の助けになれば幸いです。
(というか、もしかしてもっと簡単な方法あったりしますか...?)

multimodule by SPM、簡単にマルチモジュールを構築できてとても重宝しています。
（Xcodeくんにはもうちょっと頑張って欲しいですが...)

https://speakerdeck.com/d_date/swift-package-centered-project-build-and-practice

# 備考

他にも以下のような解決策があります。

## SPM対応のPRを投げる

一番簡単でスマートなやり方です。PRがマージされれば通常通り`Package.swift`から利用できます。が、ライブラリのメンテナンスが止まっている場合はこの方法では解決しません。

## リポジトリをforkしてSPM対応させる

作業量としては「SPM対応のPRを投げる」と同じですが、「今後このライブラリのメンテナンスは自分でやるんだ！！」と言う強い意志が必要です。

## そのライブラリを使うのをやめる
元も子もないですね。しかし、現在SPMに対応していないライブラリは既にメンテナンスされていない可能性が高いので、別のライブラリを探したり、自分で書いたりするのも一つの手だと思います。