SONICMOOV Googleページ

iOSで自作したライブラリやSDKを公開する際に気をつけたいこと

iOSで自作したライブラリやSDKを公開する際に気をつけたいこと

  • このエントリーをはてなブックマークに追加

島根ライフをエンジョイして1年になるasakaharaです。
今回は以前勉強会で発表した内容を整理して公開しています。

以前iOSでSDKの開発・公開をしたことがあるのですが、あまり手順がまとまったサイトが多くない点やiOS7など以前のOSもサポートする際に注意点がいくつかあるので整理してみました。

選択できるライブラリの種類

まずiOSで選択できるライブラリの種類は下記の通りです。

  • Xcode 5まではiOSではCocoa Touch Static Libraryのみ
  • Xcode 6からCocoa Touch Frameworkを選択することでFrameworkを簡単に作成できる

Static Libraryは以下の特徴を持っています。

  • アプリケーションのコンパイル時に組み込まれる形で(静的に)リンクされるライブラリ
  • コンパイル時に組み込まれるので、その分アプリケーションのサイズが大きくなる
  • 画像やNibなどのリソースを含むことができない

次にFrameworkについてですが、ドキュメントからの直訳をするとこんな説明になります。

動的共有ライブラリ、nib、画像、ローカライズファイル 、ヘッダーファイル、ドキュメント等のリソースファイルを1つのパッケージにまとめたディレクトリ構造のことを指します。

実際のFrameworkのディクレトリ構造はこんな感じです。
説明より実際の構造を見る方がずっと分かりやすいかと思います。

MyFramework.framework/
    Headers      ->; Versions/Current/Headers
    MyFramework  ->; Versions/Current/MyFramework
    Resources    ->; Versions/Current/Resources
    Versions/
        A/
            Headers/
                MyHeader.h
            MyFramework
            Resources/
                English.lproj/
                    Documentation
                    InfoPlist.strings
                Info.plist
        B/
            Headers/
                MyHeader.h
            MyFramework
            Resources/
                English.lproj/
                    Documentation
                    InfoPlist.strings
                Info.plist
        Current  ->; B

上記の例を見ると分かると思いますが、複数のバージョンをFramework内に含むことも可能です。
この辺りの情報は公式ドキュメントに詳しい説明があるので、もっと詳細を知りたい方はこちらを参考にしてください。

Embedded Frameworkについて

Xcode6からは新たにコードをシェアする仕組みとしてEmbedded Frameworkが登場しました。
最初に紹介したCocoa Touch Frameworkを選択することで作成することができます。

特徴としてはこんな感じです。

  • iOS8、Xcode6から使用可能
  • Frameworkを作るのがものすごく簡単になった(ビルド用のスクリプトなど必要なくなった)
  • App Extensionsを使用するケースなどを想定し、簡単に複数ターゲットから参照・利用することができるようになった

とても便利なEmbedded Frameworkですがいくつか注意点もあります。
まずDeployment Targetを8.0以降にしたアプリにしか組み込めません。
実際にはSwiftで実装する場合、frameworkのDeployment Targetを7.0にしておけば、7.0以降のアプリにも組み込みは問題なくできるんですが、App Storeへのアップロード時にエラーになってしまいます。
そのためiOS8より以前をサポートしたい場合は、Embedded FrameworkではなくStatic LibraryをObjective-Cで作成する必要があります。
ちなみにSwiftはStatic Libraryをサポートしていませんので、こちらもご注意ください。

Universal Framework

作成したFrameworkを配布する際に実機だけでなくシミュレータでも動作するようにする必要があります。
そのために実機のアーキテクチャだけでなくシミュレータのアーキテクチャを含ませることでこれを実現できます。

ここでは簡単に作成方法を紹介しておきます。

  • New -> Target -> Other -> Aggregate でUniversal Framework作成用のターゲットを作る
  • 作成したターゲット -> Build Phases -> +ボタン -> New Run Script PhaseでRun Scriptを追加

こちらが追加するスクリプトです。

!/bin/sh
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

# ビルドする
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

# iphoneos配下のframeworkをコピーする
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"

# iphoneos配下のswiftmoduleをコピーする #
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"

# lipoコマンドでUniversal binaryを作成
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}"

# プロジェクトフォルダ配下にframeworkをコピー
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"

あとはXcodeから作成したターゲットを選択してビルドすると両方のアーキテクチャに対応したFrameworkが作成されます。

Bitcode

Frameworkをビルドする際の注意点としてBitcodeについても軽くふれておきます。
iOS9からアプリ最適化の仕組みとしてApp Thinningが登場したのですが
そのためXcode7からはアプリへのBitcodeの埋め込みがデフォルトで有効になっています。
App Thinningについてはこちらに詳しく記載されています。

Frameworkを作成する際もBitcodeの埋め込みが必要になりますが、実はXcodeでArchiveを行う以外はデフォルトではBitcodeが埋め込まれないという問題があります。
例えば上記のスクリプトのようにxcodebuildコマンドでビルドする場合も同様です。
その対応方法としてBuild SettingsのUser-DefinedにBITCODE_GENERATION_MODEをbitcodeと設定することでこの問題を解決できます。

bitcode_setting

App Storeへ申請する際の注意点

最後に作成したFrameworkをアプリに組み込んで公開する際の注意点を書いておきます。
シミュレータ用のアーキテクチャであるi386やx86_64がEmbedded Frameworkに含まれていると申請時にサポートしていないアーキテクチャがあるとしてエラーになります。
エラーの詳細はこちらを見てください。

対応方法としてはアーカイブ時にRun Scriptでi386やx86_64のアーキテクチャを削除する方法などがあります。
この対応方法としてすでにいくつかの参考となるサイトがありますのでそれらを紹介しておきます。
Realmではstrip-frameworks.shという専用のスクリプトを用意してます
https://github.com/realm/realm-cocoa/blob/d59c86f11525f346c8e8db277fdbf2d9ff990d98/scripts/strip-frameworks.sh

こちらは削除方法や削除するためのスクリプトとその説明が記載してある記事で参考になります
http://ikennd.ac/blog/2015/02/stripping-unwanted-architectures-from-dynamic-libraries-in-xcode/

まとめ

ではこれまで話した注意点を踏まえたまとめです。

  • iOS8より以前をサポートするならStatic Library(静的 Framework)を使いObjective-Cで実装する
  • iOS8以降のサポートでよいならSwiftでEmbedded Frameworkを使うのが一番手軽にできる
  • 可能な限り開発者の負担を下げるためCocoaPods、Cartago(今後はSwift Package Managerも)で導入できるようにするとよい

次回はPackage Managerがどのように動いているかについて書いてみたいですね。
それではまた!

  • このエントリーをはてなブックマークに追加

記事作成者の紹介

asakahara(フロントエンドエンジニア)

島根事業所にてエンジニアをやっております。

関連するSONICMOOVのサービス

フロントエンドエンジニア募集中!

×

SNSでも情報配信中!ぜひご登録ください。

×

SNSでも
情報配信中!
SONICMOOV Facebookページ SONICMOOV Twitter SONICMOOV Googleページ
フロントエンドエンジニア募集中!

新着の記事

mautic is open source marketing automation