94
89

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Swift】Frameworkを作成する

Last updated at Posted at 2018-01-06

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」にも追加されてしまうので、削除します。
スクリーンショット 2018-01-06 13.28.13.png

それではフレームワークの機能を使ってみます。
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」を選択。
スクリーンショット 2018-01-06 13.43.30.png

スクリプトをコピペします。
スクリーンショット 2018-01-06 13.46.42.png

######################
# 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」フレームワークを削除しておきます。

「Add Files to "Jack"...」を選択。
スクリーンショット

「Sally.xcodeproj」を選択すると、こんな感じのフォルダ構成になります。
先ほど確認した場所とは違う場所にフレームワークを格納するようで、「Sally.framework」が赤字になっています。
スクリーンショット

ターゲットを「Sally」にしてシミュレーター、実機用それぞれビルド。
ターゲットを「Sally-Universal」にしてビルド。
スクリーンショット 2018-01-06 14.18.08.png

ここで新しく作成した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.swiftviewDidLoadSally().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(作成編)

間違っている点や改善点などありましたら、ぜひフィードバックをお願いします。

94
89
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
94
89

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?