Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
13
Help us understand the problem. What is going on with this article?
@kazuhiro4949

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

はじめに

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

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

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

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

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

Tips

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

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

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

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

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

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

mlmodelはコンパイルしておく必要がある

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

xcrun coremlcompiler compile HogeModel.mlmodel HogeModel.mlmodelc

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

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バンドルは使わないように注意していると思います。
そのため、以下の初期化子を使っている人が多いかなと思います。

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

  • Bundle.moudle

APIドキュメントに載ってないけど、これはなんだろう?」って思うかもしれません。
こいつはSwiftPM専用のStatic変数で、Package.swift経由で導入したリソースを取得するためのものです。

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

#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

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

Storyboard上ではinherit module from targetをつけなければならない

よっしゃ全部にModule名つけた!完璧だ!ってなってもまだ駄目です。
その下のInherit Module From Targetもちゃんと全部チェックつけていきましょう。
そうしないと同じくクラッシュの原因になります。

スクリーンショット 2020-12-17 21.12.13.png

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

まとめ

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

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

13
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
13
Help us understand the problem. What is going on with this article?