1.はじめに
いくつかプロジェクトを作成しているとコードを使いまわしたいことが出てくると思います。そんな時に便利なのがフレームワーク。一度作成したら、各プロジェクトにインポートするだけで使えるようになります。
フレームワークの作成、デバッグ方法をまとめたいと思います。
2.環境
Xcode Version 9.2
Swift Version 4.0.3
3.前提
当記事ではプロジェクト名を以下の通りにしています
フレームワーク : Sally
フレームワークを使用するプロジェクト : Jack
4.フレームワークの作成
File > New > Projectから「Cocoa Touch Framework」を選択。
「Product Name」を「Sally」にして作成。
「Sally」フォルダの中にファイルを追加します。このファイルにフレームワークとして作成したい機能を実装します。
File > New > File から「Cocoa Touch Class」を選択。
クラス名を「Sally」にします。
フォルダ構成はこのようになります。
以下のように作成したクラスに機能を実装していきます。
ポイントはクラス、メソッドともに public
にすることです。
public
にすることでフレームワーク外からアクセスできるようにします。
// Sally.swift
public class Sally: NSObject {
public func sallyMethod() {
print("Sally().sallyMethod is called !!")
}
}
ビルドします。ビルドすることでプロジェクトで使用できる状態のフレームワークを生成します。
ポイントはシミュレーター用と実機用で別々に作成する必要があることです。
シミュレーター、実機共通のフレームワークの作成も後々説明しますが、その場合も一度それぞれのフレームワークを作成する必要があります。
まずは実機用のフレームワークを作成します。
ターゲットに実機か「Generic iOS Device」を選択。
ビルド前はまだファイルがないので「Sally.framework」が赤字になっています。
「command + B」 でビルドします。
ビルドに成功すると「Sally.framework」が黒字になります。
次にシミュレーター用のフレームワークを作成します。
ターゲットにシミュレーター(iPhone X でもiPhone 8 Plusでもなんでもいいです)を選択。
実機用を作成した時と同様に「command + B」でビルドします。
作成したフレームワークは「Sally.framework」を選択して「Show in Finder」で確認することができます。
「Debug-iphoneos」というフォルダ内のフレームワークが実機用、「Debug-iphonesimulator」というフォルダ内がシミュレーター用です。
5.フレームワークを使用する
ここまでで作ったフレームワークを使ってみます。
File > New > Project を選択し、「Single View App」でプロジェクトを新規作成します。
プロジェクト名は「Jack」にしました。
プロジェクトに先ほど作成したフレームワークをインポートします。
「Jack」をシミューレーターで確認する場合は「Debug-iphonesimulator」フォルダ内のフレームワークを、実機で確認する場合は「Debug-iphoneos」フォルダ内のフレームワークをドラッグ&ドロップでインポートします。
「Copy Items if needed」をチェックしない場合、フレームワークのコピーは取り込まれずリンクがされます。
この場合、「Build Setting」の「Search Paths」> 「Framework Search Paths」に適切なパスが設定されていないとビルドできません。
次にインポートしたフレームワークを「Embedded Binaries」にドラッグします。
この時、「Linked Frameworks and Libraries」にも追加されてしまうので、削除します。
それではフレームワークの機能を使ってみます。
Sally
フレームワークをインポートし、 viewDidLoad
内でフレームワークのメソッドを実行します。
//ViewController.swift
import UIKit
import Sally
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Sally().sallyMethod()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ランしてみましょう。
コンソールに「Sally().sallyMethod is called !!」と表示されれば成功です!
6.シミュレーターと実機の共通のフレームワークを作成する
先ほど作成したフレームワークはシミュレーター用と実機用とで別々でした。フレームワークを使用するプロジェクトをどちらで確認するかによってインポートするフレームワークを変える必要があります。
そこで、共通のフレームワークを作成することにします。
作業するプロジェクトは再び「Sally」です。
「Sally」に新しいターゲットを追加します。
File > New > Target から「Aggregate」を選択。
プロジェクト名を「Sally-Universal」にしました。
次に「Sally-Universal」の「Build Phases」で「+」のアイコンから「New Run Script Phase」を選択。
######################
# Options
######################
REVEAL_ARCHIVE_IN_FINDER=false
FRAMEWORK_NAME="${PROJECT_NAME}"
SIMULATOR_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${FRAMEWORK_NAME}.framework"
DEVICE_LIBRARY_PATH="${BUILD_DIR}/${CONFIGURATION}-iphoneos/${FRAMEWORK_NAME}.framework"
UNIVERSAL_LIBRARY_DIR="${BUILD_DIR}/${CONFIGURATION}-iphoneuniversal"
FRAMEWORK="${UNIVERSAL_LIBRARY_DIR}/${FRAMEWORK_NAME}.framework"
######################
# Build Frameworks
######################
xcodebuild -workspace ${PROJECT_NAME}.xcworkspace -scheme ${PROJECT_NAME} -sdk iphonesimulator -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator 2>&1
xcodebuild -workspace ${PROJECT_NAME}.xcworkspace -scheme ${PROJECT_NAME} -sdk iphoneos -configuration ${CONFIGURATION} clean build CONFIGURATION_BUILD_DIR=${BUILD_DIR}/${CONFIGURATION}-iphoneos 2>&1
######################
# Create directory for universal
######################
rm -rf "${UNIVERSAL_LIBRARY_DIR}"
mkdir "${UNIVERSAL_LIBRARY_DIR}"
mkdir "${FRAMEWORK}"
######################
# Copy files Framework
######################
cp -r "${DEVICE_LIBRARY_PATH}/." "${FRAMEWORK}"
######################
# Make an universal binary
######################
lipo "${SIMULATOR_LIBRARY_PATH}/${FRAMEWORK_NAME}" "${DEVICE_LIBRARY_PATH}/${FRAMEWORK_NAME}" -create -output "${FRAMEWORK}/${FRAMEWORK_NAME}" | echo
# For Swift framework, Swiftmodule needs to be copied in the universal framework
if [ -d "${SIMULATOR_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/" ]; then
cp -f ${SIMULATOR_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/* "${FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" | echo
fi
if [ -d "${DEVICE_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/" ]; then
cp -f ${DEVICE_LIBRARY_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule/* "${FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" | echo
fi
######################
# On Release, copy the result to release directory
######################
OUTPUT_DIR="${PROJECT_DIR}/Output/${FRAMEWORK_NAME}-${CONFIGURATION}-iphoneuniversal/"
rm -rf "$OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
cp -r "${FRAMEWORK}" "$OUTPUT_DIR"
if [ ${REVEAL_ARCHIVE_IN_FINDER} = true ]; then
open "${OUTPUT_DIR}/"
fi
ターゲットを「Sally-Universal」にしてビルドをしてフレームワークを作成します。
先ほど別々のフレームワークを作成した時と同様に「Show in Finder」で作成したフレームワークを確認します。
「Debug-iphoneuniversal」というフォルダに作成されたことが確認できます。
このフレームワークを「Jack」プロジェクトにインポートすることでシミュレーターでも実機でも同一のフレームワークが使えるようになります。
7.フレームワークとアプリを一緒に作成する
後々別のアプリでも使用することが想定される場合、アプリと同時にフレームワークを作成してデバッグしたい。そんなことができたら便利!!
今回は「Jack」プロジェクトで同時に「Sally」フレームワークを作成します。
まず、「Jack」プロジェクトから「Sally」フレームワークを削除しておきます。
「Sally.xcodeproj」を選択すると、こんな感じのフォルダ構成になります。
先ほど確認した場所とは違う場所にフレームワークを格納するようで、「Sally.framework」が赤字になっています。
ターゲットを「Sally」にしてシミュレーター、実機用それぞれビルド。
ターゲットを「Sally-Universal」にしてビルド。
ここで新しく作成したUniversalな「Sally」フレームワークを「Jack」プロジェクトに取り込みます。
「Embedded Binaries」への追加と「Build Setting」の「Framework Search Paths」への追加をお忘れなく。
さて、フレームワークの更新をしてみます。
Sally
クラスに sallyMethod2
を追加します。
// Sally.swift
import UIKit
public class Sally: NSObject {
public func sallyMethod() {
print("Sally().sallyMethod is called !!")
}
public func sallyMethod2() {
print("Sally().sallyMethod2 is called !!")
}
}
これまでと同様に実機用ビルド、シミュレーター用ビルド、Universal用ビルドを実行します。
「Jack.swift」の Jack.swift
の viewDidLoad
に Sally().sallyMethod2()
を追加します。
「Jack」プロジェクトでフレームワークの機能を使用します。
// Jack.swift
import UIKit
import Sally
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Sally().sallyMethod()
Sally().sallyMethod2()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Sally
クラスの sallyMethod2
でブレークポイントを設定してから、「Jack」をランします。
これでアプリもフレームワークも同時に作成できますね。
8.最後に
フレームワークの作成が終わったら、「Sally.xcodeproj」を削除します。
「Sally.xcodeproj」単体で開いてビルドしてフレームワークを生成します。
生成したフレームワークを「Jack」プロジェクトに取り込めば、「Jack」プロジェクトから「Sally」プロジェクトを削除した上で、フレームワークを使用することができます。
9.課題というか疑問
生成したフレームワークは「Debug-○○○」というフォルダに格納されます。
「Debug」。。。ものすごく気になります。
アプリをリリースする際はこのフレームワークもリリース用にビルドしなければならないのかどうか。
今後試してみたいと思います。
10.参考
[Xcode6] SwiftでCocoa Touch Frameworkを作る
[Xcode6] Universal Frameworkを作る
[Xcode6] iOSアプリプロジェクト内にframeworkプロジェクト
Xcode7.3 & SwiftでつくるCocoaTouch Framework(作成編)
間違っている点や改善点などありましたら、ぜひフィードバックをお願いします。