アプリを3つリリースしているのですが、違うアプリだけど同じ機能 ってのが結構あるんですよね。
「カスタムアラート」とか「お問い合わせ」とか「開発ブログ」とか。
2つまではコピー実装でも別に良かったんですけど、最近は面倒くさいんですよね
いっそ Framework にしてみるか!って思い立つも、ドンピシャな記事が探せんのです。
Storyboard で作成した画面込みで Framework を作りたいのです。
そして検索能力絶賛低下中の私なのです ( ˘ω˘ )スヤァ
まぁ必死こいて実装したので、ついでに記事も作りましょうっつーワケです。
開発環境
・Xcode12.4
・Swift5
Framework にぶち込む機能を実装
プロジェクトの作成
まずは Xcode 起動してーの、
File → New → Project → iOS → Framework を選択して、プロジェクト名をつけます。
ここでは OriginalFramework とします。
クラスを作成
⌘ + N で OriginalFrameworkVC.swift を作成(適当な名前をどーぞ)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import UIKit public class OriginalFrameworkVC: UIViewController { override public func viewDidLoad() {} // 初めて Framework を作る方のためのメソッドなので、いらない人はスルーで。 public func test1() { print("Test1") } func test2() { print("Test2") } // 生成する時は以下のメソッドを使用 public static func initWithStoryboard() -> OriginalFrameworkVC { let storyboard = UIStoryboard(name: String(describing: OriginalFrameworkVC.self), bundle: Bundle(for: OriginalFrameworkVC.self)) let vc = storyboard.instantiateInitialViewController() as! OriginalFrameworkVC return vc } } |
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 を選択して、以下のコードをコピペ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#!/bin/sh UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal OUTPUT_FOLDER=${PROJECT_DIR} # make sure the output directory exists mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" # シミュレータ用・実機用の Framework を作成(bitcode を含む) xcodebuild BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" -workspace ${PROJECT_NAME}.xcworkspace -scheme ${PROJECT_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" -UseModernBuildSystem=NO clean build xcodebuild BITCODE_GENERATION_MODE=bitcode OTHER_CFLAGS="-fembed-bitcode" -workspace ${PROJECT_NAME}.xcworkspace -scheme ${PROJECT_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" -UseModernBuildSystem=NO clean build # iPhoneビルドのFramework構造をコピー cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/" # iPhoneSimulatorビルドのSwiftモジュールをコピー cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule" # Universalなバイナリファイルを作成して、実行可能ファイルを作成 lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}" # 使いやすいようにプロジェクトディレクトリにフォルダごとコピーして、Finderで開く cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${OUTPUT_FOLDER}" open "${OUTPUT_FOLDER}" |
実際に 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 できる準備が整いました。
実際にアクセスしてみる
1 2 3 4 5 6 7 8 9 10 11 12 |
import UIKit import OriginalFramework class SampleViewController: UIViewController { override func viewDidLoad() { let vc = OriginalFrameworkVC.initWithStoryboard() vc.test1() // アクセス可能 vc.test2() // アクセス不可 } } |
test1() のアクセス修飾子は public なので、コールすることが出来ます。
test2() のアクセス修飾子はデフォルト、つまり internal となるので、コールすることが出来ません。
導入した側のプロジェクトから扱いたいクラスや構造体などは、アクセス修飾子を public か open にしておく必要があります。
ここだけ注意してください!
補足
これは私の場合ですが、導入した側のプロジェクトを Archive した際、以下のエラーが出ました。
自作の Framework 側のプロジェクトの Enable Bitcode は YES です。
もちろん導入した側のプロジェクトも。
この場合、Framework 側のプロジェクトの Target → Build Settings で以下の作業をします。
1)Other C Flags に -fembed-bitcode を追加
2)User-Defined → BITCODE_GENERATION_MODE を新規追加、value は bitcode
これで解消しました。
もし同じエラーになった方いたらお試しください。
さいごに
見返すと簡単なんですけど、Xcode のバージョンが違うとエラーになる事もあり、結構大変でした (((( ˙-˙ ))))
久々にしんどぅーい作業でした _:(´ཀ`」 ∠):_
でもこれで共通機能をバンバン作れるぜぇ…
コメント