【Swift5】自作画面を Framework で再利用する

アプリを3つリリースしているのですが、違うアプリだけど同じ機能 ってのが結構あるんですよね。
「カスタムアラート」とか「お問い合わせ」とか「開発ブログ」とか。
2つまではコピー実装でも別に良かったんですけど、最近は面倒くさいんですよね

いっそ Framework にしてみるか!って思い立つも、ドンピシャな記事が探せんのです。
Storyboard で作成した画面込みで Framework を作りたいのです。
そして検索能力絶賛低下中の私なのです ( ˘ω˘ )スヤァ

まぁ必死こいて実装したので、ついでに記事も作りましょうっつーワケです。

開発環境

・Xcode12.4
・Swift5

Framework にぶち込む機能を実装

プロジェクトの作成

まずは Xcode 起動してーの、
File → New → Project → iOS → Framework を選択して、プロジェクト名をつけます。
ここでは OriginalFramework とします。

クラスを作成

⌘ + N で OriginalFrameworkVC.swift を作成(適当な名前をどーぞ)

UIStoryboard(name:bundle:) の bundle が Bundle(for: OriginalFrameworkVC.self) となっていますが、
nil を入れると導入したプロジェクトの Bundle を参照してしまい、クラッシュします。

ストーリーボードを作成

⌘ + N で OriginalFrameworkVC.storyboard を作成。
以下の作業を行う。
1)ViewController を設置
2)CustomClass に OriginalFrameworkVC を設定
3)Is Intial View Controller にチェックを付ける

気になる人は、画面遷移したことが分かりやすいよう UILabel 置くとか、背景色を変えてください。

プロジェクトを Framework として書き出す

通常の Framework の書き出しは ビルドするだけ でも可能ですが、
開発環境用の実機とシミュレータ、本番環境用の実機とシミュレータの計4つを書き出す必要があります。

この方法でも問題はないのですが、それらを一つにまとめることができます。
それが Universal-Framework というものです。
手間は増えますが、4つ書き出すよりも1つの方が無駄なもん増えずに済みますし。
スッキリしそうなので、今回はこの方法でいきます _(:3」∠)_

UniversalFramework 専用のターゲットを作る

File → New → Target → Other → Aggregate を選択。
Next を押して、Product Name を「プロジェクト名-Universal」にして新規追加します。
ここでは「OriginalFramework」というプロジェクト名だったので、「OriginalFramework-Universal」とします。
別に決まりじゃないので、好きな名前で結構です。

スクリプトを追加する

上記で追加した Target(ここでは OriginalFramework-Universal です)にスクリプトを追加します。
1)TARGETS → OriginalFramework-Universal を選択
2)Build Phases を選択
3)New Run Script Phase を選択して、以下のコードをコピペ

実際に Framework を書き出す

シミュレータと実機のバイナリファイルを作成して、2つのバイナリをがっちゃんこさせます。
1)Target を OriginalFramework 、デバイスをシミュレータにしてビルド
2)Target を OriginalFramework 、デバイスを実機にしてビルド
3)Target を OriginalFramewor-Universal 、デバイスはシミュレータでも実機でも大丈夫

これで、Framework が作られました |・ω・)ノ
プロジェクトフォルダに Framework が出来てるはずなので、ご確認を。

他のプロジェクトから Framework を使う

作った Framework を使いたいプロジェクトがある場合はそのプロジェクト、ない場合は新規プロジェクトを作成してください。

ドラッグ&ドロップでコピーする

Archive で作られた Framework が Finder で開かれるので、プロジェクトにドラッグ&ドロップしてください。
その際、Copy items if needed. にチェックを入れてください。

Embed Framework を設定する

Target → Build Phases → Embed Framework → + → ドラッグ&ドロップしたFramework を選択
上記を行うことで、import できる準備が整いました。

実際にアクセスしてみる

test1() のアクセス修飾子は public なので、コールすることが出来ます。
test2() のアクセス修飾子はデフォルト、つまり internal となるので、コールすることが出来ません。

導入した側のプロジェクトから扱いたいクラスや構造体などは、アクセス修飾子を public か open にしておく必要があります。
ここだけ注意してください!

補足

これは私の場合ですが、導入した側のプロジェクトを Archive した際、以下のエラーが出ました。

ld: bitcode bundle could not be generated because ‘FrameworkPath’ was built without full bitcode.

自作の Framework 側のプロジェクトの Enable Bitcode は YES です。
もちろん導入した側のプロジェクトも。

この場合、Framework 側のプロジェクトの Target → Build Settings で以下の作業をします。
1)Other C Flags に -fembed-bitcode を追加
2)User-Defined → BITCODE_GENERATION_MODE を新規追加、value は bitcode

これで解消しました。
もし同じエラーになった方いたらお試しください。

さいごに

見返すと簡単なんですけど、Xcode のバージョンが違うとエラーになる事もあり、結構大変でした (((( ˙-˙ ))))
久々にしんどぅーい作業でした _:(´ཀ`」 ∠):_

でもこれで共通機能をバンバン作れるぜぇ…

コメント