iOS7対応アプリでSwiftコンパイルの高速化

はじめまして、Couples iOSの開発をしている木村です。
Swiftがオープンソース化され、今まで以上にSwiftへ注目が集まっていますね!

Swift × Couples iOS

Couples iOSではクラッシュの少ないアプリを提供するため、Swiftを積極的に採用しています。
Swiftが公開された当初からObjective-Cで書かれていたCouples iOSの置き換えをはじめ、言語仕様が安定しない中の開発となりましたが、今ではプロジェクト全体で6割強のソースコードがSwiftで書かれたものとなっています。

Screen Shot 2015-12-17 at 6.54.31 PM

※GitHub, Language Details抜粋

積極的に採用をすすめたSwiftですが、言語仕様以外にも難点が幾つか存在しています。今回はそのうちの一つ「ビルドが遅い問題」を書こうと思います。

問題:ビルドが遅い(iOS7サポート)

Couples iOSアプリはiOS7のサポートをしているため、Module (Dynamic Library) をプロジェクトで使用することができません。
また、SwiftファイルはStatic Libraryとしてコンパイルすることも出来ないため、全てのSwiftファイルをCouples iOSプロジェクトに含めてビルドをする必要が生じてしまいます。
その影響により、Couples iOSプロジェクトをコンパイル時キャッシュオブジェクトが無い状態でビルドをすると、ビルドに要する時間が15分弱掛かってしまいます。

解決案:Module利用のターゲットを作成

ビルドに時間が掛かるのはコード量からすると仕方ありませんが、開発の時だけでも効率を上げる開発用のターゲットを用意することにしました。

  • 申請用 & iOS7の開発用のターゲット
  • 開発用 (iOS8, iOS9) Moduleを利用したターゲット

これにより、開発用ターゲットではビルド所要時間を最大で5分削減することに成功しました。次に対応方法を記載しておきますので、iOS7サポートを続けている開発者の方は是非参考にしてください。

1.ターゲットを複製する

まず、申請用に使用しているターゲットを複製して新しくターゲットを作成します。

Screen Shot 2015-12-18 at 10.23.03 AM

「Duplicate」を選択すると、「Duplicate and Transition to iPad」(iPad用に複製しますか)と促されますが、「Duplicate Only」を選択します。

Screen Shot 2015-12-08 at 12.28.48 PM

今回は新しいターゲットの名前を「Couples-moduled」としています。

Screen Shot 2015-12-18 at 10.25.30 AM

2. Module用のConfigurationを作成

ターゲットごとに実行時の設定を変更するためConfigurationも複製します。

プロジェクトファイルから [Project] → [Info] を開き、下図のように [+] にてConfigurationの複製を行います。

Screen Shot 2015-12-18 at 10.45.24 AM

Couples-moduledターゲットの [Build Settings] から Other Swift Flags の値に -D USE_MODULE を登録しておきます。
後ほど、Moduleのインポートを分けるための使用を説明します。

Screen Shot 2015-12-18 at 11.00.27 AM

3. Podfileを複数ターゲットに対応

CocoaPodsを複数ターゲットに対応させるためPodfileを修正します。下記にサンプルを記載しておきます。

def common_pods
  pod 'AFNetworking', '2.6.1'  
end
​
target 'Couples' do
  platform :ios, '7.0'
  inhibit_all_warnings!

  # 共通のPods
  common_pods
end
​
target 'Couples-moduled' do
  platform :ios, '8.0'
  inhibit_all_warnings!
  use_frameworks!

  # SwiftのPods
  pod 'CoreStore', git: 'https://github.com/JohnEstropia/CoreStore.git', commit: '578e4966fc87a10ebbf1bb0c4eb13e88696dc527'
  pod 'GCDKit', git: 'https://github.com/JohnEstropia/GCDKit.git', tag: '1.1.4'
​
  # 共通のPods
  common_pods
end

Couples-moduled の方はフレームワークを使用する宣言をするため、use_frameworks!を呼び出しておきます。
また、Couplesプロジェクトでは一部のソースをgit-submoduleで取り込んでいるため、それらも Couples-moduled ターゲットでPodsとして取り込むようにします。
※submoduleとPodfileのバージョン(コミットハッシュ)は同一のものにしておきます

Podfile修正後はPodsを更新するためにpod installを行い、Couples-moduledターゲットにCouples-moduledで使用するxcconfigファイルを設定します。

4. ソースコードにmoduleのimportを記述

ターゲットとConfigurationの準備は整い、あとはソースコードを修正するのみです。
ソースコードは共通のため、用意した USE_MODULE フラグを利用してModuleのインポートをコンパイル時に制御します。

import UIKit

#if USE_MODULE
    import SwiftyJSON
#endif

5. スキームの作成

最後にビルドするためのスキームを作成します。これも、元のスキームから [Duplicate Scheme] にて複製をし作成します。

Screen Shot 2015-12-14 at 10.13.26 AM

名前を「Couples-moduled」に変更し、以下のスクリーンショットのようにConfigurationとExecutableを変更します。

  • Build Configuration:Debug-moduled
  • Executable: Couples-module.app

Screen_Shot_2015-12-14_at_10_14_13_AM


Module用ターゲットの作成はこれにて完了です。
Couples-moduledスキームでビルドを行うと、Podsに登録したライブラリはDynamic Libraryとしてコンパイルされるようになります。

まとめ

メリット

iOS7の検証時は今までどおり申請用のターゲットを使用して行う必要がありますが、開発・検証の多くはiOS8, 9に割く時間が多いと思います。
コンパイル時間を数分でも速くなれば他の作業に時間をあてることが可能になるため、ターゲットを分ける価値が存分にあります。

少し先の話になりますが、iOS7のサポートを終了した段階で Couples-moduled をそのまま申請用へとすることができるため、今後の対応を先取りすることにもなります。

デメリット

今回のターゲットを分ける懸念点は、ターゲットを申請用と開発用として分けることにより設定項目の管理が複雑化することです。
実際、Couplesプロジェクトでは xcconfig を使って設定値を共有することによりカバーしています。


以上、iOS7をサポートしているアプリでSwiftを使う場合のビルド時間短縮のTipsでした!

補足

補足1: Swiftのコンパイルについて

Swiftのコンパイルは同じModule内のクラスファイルであれば、import文が不要なのでアプリ内のクラスファイルが増加するほど依存関係を考慮する必要が生じ、1ファイルあたりのコンパイル時間が増加してしまいます。
これをModuleで細分化することによって、import文を明示的に指定し、依存関係を考慮することを減らしています。
また、Moduleのコンパイルは並列に行うことが可能なため、ビルド時間の高速化が可能となっています。

補足2: マシンスペック

開発時のマシンはMacBook Proでフルスペックです。

spec

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

エウレカでは、一緒に働いていただける方を絶賛募集中です。募集中の職種はこちらからご確認ください!皆様のエントリーをお待ちしております!

Recommend

いつも初心を忘れない!デザインの質を確実に上げるための制作フロー

Terraformを1年間運用して学んだトラブルパターン4選