iOS用ライブラリ作成者向けSwift Package Managerのリソース周りTips

# はじめに
最近は多くのiOSアプリ用ライブラリでSwift Package Manager(SwiftPM)に対応してきています。
Xcode11でSwiftPMのサポートが充実し導入しやすくなり、
Swift5.3からはコード以外のリソース類もSwiftPMで管理できるようになりました。

- [Adopting Swift Packages in Xcode
](https://developer.apple.com/videos/play/wwdc2019/408/)
- [Swift packages: Resources and localization](https://developer.apple.com/videos/play/wwdc2020/10169/)

今では、もう普通に使えるんじゃないかって感じで利用している人も多いと思います。

自分としてはXcodeに組み込まれたことでXcodeでライブラリを自動管理できるようになったことがかなり大きく、
今では基本的にはライブラリをSwiftPMで導入しています。

そんな感じでとても便利に使えるSwiftPMなのですが、作る側からするとどうでしょうか？
いくつかのライブラリでSwiftPM対応した経験から、リソース周りで結構癖が強いなということを感じました。

そこで実際にすんなりいかなかったポイントをTipsとしてまとめておきます。

# Tips
## 自動的に入るリソースの種類は限られている

そもそもリソースを含んだプロジェクトで```Package.swift```を作ったらどうやってリソース指定するの...?という感じなのですが、何も指定しなくて大丈夫です。
```target```の```Source```以下に```xcassets```や```storyboard```などを突っ込んでおけば、Xcode上でライブラリを導入したときに勝手に入れてくれます。

```swift
let package = Package(
    name: "HogeLibrary",
    platforms: [
        .iOS(.v14)
    ],
    products: [
        .library(name: "HogeLibrary", targets: ["HogeLibrary"])
    ]
    targets: [
        .target(name: "HogeLibrary", path: "Sources")
    ]
)
```

しかし、勝手に入ってくれるものはごく一部で、例えばjsonファイルなどは含まれません。
そういったファイルの場合どう対応すればいいでしょうか？
```target```に```resources:```を追加する必要があります。

```swift
targets: [
    .target(name: "HogeLibrary", path: "Sources", resources: [.copy("./hoge.json")])
]
```

リソースがうまく入っていなかった場合はこのような対応を試してみましょう。

## mlmodelはコンパイルしておく必要がある
iOS的に特殊に扱われるリソースであれば大丈夫なのかと思って高をくくっていると、思わぬところで躓いてしまいます。
自分の場合は、```.mlmodel```を持っているライブラリをSwiftPMへ対応するときに面倒なことになりました。
```.mlmode```lはSwiftPMで自動的にライブラリとして導入することはできません。
では、どうするかときうと事前に```.mlmodelc```へコンパイルしておく必要があります。

```
xcrun coremlcompiler compile HogeModel.mlmodel HogeModel.mlmodelc
```

そしてjsonの時と同じように、```resources```として指定します。

```swift
targets: [
    .target(name: "HogeLibrary", path: "Sources", resources: [.copy("./HogeModel.mlmodelc")])
]
```

しかしここで不都合が発生してしまいます。
```.mlmodelc```にして導入したということは、IFである```.swift```クラスの自動生成などは導入側では行われません。
そのためSwiftPMで```.mlmodel```を入れるのであれば、一旦自動生成されたmlmodelのIFとなるSwiftクラスをコピーしてきてそのクラスへ```.mlmodelc```を渡すことで初期化する必要があります。

特定のパッケージマネージャーに対応するためにコードの修正が必要になるというちょっと面倒なケースになります。

## Bundleの指定はSwift Flagで出し分ける必要がある
上述の```.mlmodel```はライブラリとして持つことは特殊なケースなのかもしれません。
普通はstoryboardやxibなどが多いと思うので特に何も考えなくていいかなと思うかもしれませんが、そうはいきません。

Storyboardが自動で入るのはいいものの、いざそのStoryboardを```UIStorybard```クラスで初期化しようとするとクラッシュします。

理由としてはSwiftPMはSwiftPM専用のBundleを指定する必要があるからです。
ライブラリを作る場合、```.main```バンドルは使わないように注意していると思います。
そのため、以下の初期化子を使っている人が多いかなと思います。

- [Bundle.init(for:)](https://developer.apple.com/documentation/foundation/bundle/1417717-init)
- [Bundle.init(identifier:)](https://developer.apple.com/documentation/foundation/bundle/1411929-init)

SwiftPMの場合はこれらではnilが返ってきます。ではどうするのかというとその代わりに↓を使います。

- Bundle.moudle

「[APIドキュメント](https://developer.apple.com/documentation/foundation/bundle)に載ってないけど、これはなんだろう？」って思うかもしれません。
こいつはSwiftPM専用のStatic変数で、Package.swift経由で導入したリソースを取得するためのものです。

- [Bundling Resources with a Swift Package](https://developer.apple.com/documentation/swift_packages/bundling_resources_with_a_swift_package)

SwiftPM専用のものなのでXcode上で直接呼び出してもエラーになってしまいます。使うときは↓のようにします。

```swift
#if SWIFT_PACKAGE
      return Bundle.module
#else
      return Bundle(for: HogeViewController.self)
#endif
```

この辺りもSwiftPM用にコードの書き換えが必要になってくるため、ちょっと面倒なところではあります。

## Storyboard上ではModuleを明示しないければならない
これは単にひたすら対応がめんどくさいやつです。SwiftPMではStoryboard・XIB上の↓の部分を明示的に指定しないと、IBOutletのプロパティがnilになってしまいます。
(ほとんどのケースがそうだと思いますが)IUOとして定義されている場合、一個でも指定漏れがあるとクラッシュを引き起こします。

![スクリーンショット 2020-12-17 21.10.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29477/4e36625b-b581-c92a-3ec9-bae98f97f609.png)

丁寧に対応していきましょう。

## Storyboard上ではinherit module from targetをつけなければならない
よっしゃ全部にModule名つけた！完璧だ！ってなってもまだ駄目です。
その下の```Inherit Module From Target```もちゃんと全部チェックつけていきましょう。
そうしないと同じくクラッシュの原因になります。

![スクリーンショット 2020-12-17 21.12.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29477/1a95cd07-210e-9e39-2c05-e8053823a85c.png)

これも丁寧に対応していきましょう。

# まとめ
他にも、xcodeからdynamic frameworkとして作った場合に設定が反映されない、などの細かいハマリポイントなどもあります。
この辺は慣れもありますが、やってみると「あれ...？SwiftPMちょっと扱いにくいかも...」と感じるかもしれません。

ただし、使う側としてはSwiftPM対応されたライブラリはとても導入しやすいので、もっとどんどん対応されたライブラリが増えるとありがたいな！と思っています(笑)