Edited at

【XcodeGen】で、AdHoc配信までできるiOSプロジェクトを作った話


tldr


  • xcodegenでちゃんとAdHoc配信までできる設定をするのは結構大変

-> なので、自分で頑張って作ってみました。あとXcodeGenの考察を色々します。


はじめに

どうも。新卒1年目のiOSエンジニアです。


  • XcodeGenでプロジェクト管理をする

  • 完成したアプリはdeploygateで配信する

という要件のiOSプロジェクトに先日まで参加していたのですが、色々苦労したので、そのときの話を語ります。


そもそもXcodeGenって

iOSプロジェクトを複数人で開発する場合、プロジェクトのファイルを管理する.xcodeprojファイルのコンフリクトがよく起こります。大規模開発だとよく起こるトラブル(らしい)です。

これを解消するのがXcodeGenで、プロジェクトのソースやビルド設定などをyamlファイルで定義し、yamlファイルとソースコードから開発環境となる.xcodeprojファイルを一意に生成できるツールです。即ち、.xcodeprojがgit上に無くても、yamlファイルさえあれば開発環境を他の開発者のPCで再現できるようになります。

.xcodeprojファイルを共有せずに済む仕組みとツールがあれば、そもそもコンフリクトとか起こらないよね! :smile: という寸法です。


Xcodegenを使う


XcodeGenでgenerateするまで

READMEを見れば大体わかりますが、インストールして、

$ brew install xcodegen

依存しているライブラリとソースコードを入れるディレクトリの準備をして

(ソースコードを入れるディレクトリは先に用意しないとxcodegen generateできません)

$ cd (プロジェクトルート)

$ mkdir (ソースコードを入れるディレクトリ)

$ pod init
$ vi Podfile
$ pod install

$ vi Cartfile
$ carthage bootstrap --no-use-binaries --cache-builds --platform ios

yamlファイルを書いて (後述)

$ vi project.yaml

generateします。歯車3つ出てきたら成功です :tada:

$ xcodegen generate

Loaded project:
Name: testProject
Targets:
testProject: iOS application
⚙️ Generating plists...
⚙️ Generating project...
⚙️ Writing project...
Created project at ($PROJECT_ROOT)/.xcodeproj

$ ls
$PROJECT_NAME Package.swift Pods Sources project.yml
Cartfile Podfile README.md Tests testProject.xcodeproj

生成されたら容赦無くプロジェクトを開いてやりましょう。CocoaPodsを使う場合は.xcworkspaceも一緒に生成されているので無問題です :thumbsup:

$ open testProject.xcworkspace


project.yamlを書く


配信しない(とりあえずprojectファイルを生成して開発したい)場合

これ↓だけあればとりあえずprojectファイルが作れます。


project.yaml

name: testProject

deploymentTarget:
iOS: "11.0"
targets:
testProject:
platform: iOS
type: application
sources:
- path: testProject
- path: testPeoject/R.generated.swift
optional: true
info:
path: testProject/Info.plist
properties:
CFBundleDevelopmentRegion: ja_JP
UISupportedInterfaceOrientations: [UIInterfaceOrientationLandscapeRight]
UIRequiresFullScreen: "YES"
UILaunchStoryboardName: "LaunchScreen"
NSCameraUsageDescription: "カメラ撮影をするため"
CFBundleDisplayName: "AppName"
CFBundleIdentifier: "com.skamada.testProject"
dependencies:
- carthage: RxSwift
- carthage: RxCocoa
- carthage: RxRelay
- carthage: SwiftyBeaver
- carthage: Reachability
- carthage: Nuke
preBuildScripts:
- path: /bin/sh
name: Run R.swift
inputFiles:
- $TEMP_DIR/rswift-lastrun
outputFiles:
- $SRCROOT/$PROJECT_NAME/R.generated.swift
script: |
"Pods/R.swift/rswift" generate "$SRCROOT/testProject/R.generated.swift"

ここで使っているのは



  • name プロジェクト名。


  • deploymentTarget 対応する(buildする)OSバージョン



    • sources ソースコードを格納するディレクトリ名。ディレクトリはあらかじめ作っておきましょう。


    • optional R.swiftのR.generated.swiftなど、ビルドして初めて生成されるファイルはoptional指定が必要です。


    • info Info.plistに設定したいことを羅列します。CF~~~とかUI~~~とか、Row Keysでの指定が必要です。XcodeのInfo.plistで右クリック > Show Row Keys/Valuesで1つずつ確認してみましょう。Info.plistの生成からしてくれます。


    • dependencies 依存するcarthageライブラリや、他のターゲット(target)、フレームワーク (framework)、sdk(sdk)を指定すれば、勝手に設定してくれます。


    • preBuildScripts R.swiftのように、ビルドの前後にスクリプトを走らせたい場合は設定しましょう。postBuildScriptsとか、postCompileScriptsとかもあります。




AdHoc配信をしたい(証明書、プロビ周りの設定をしたい)場合

こんなの↓を追加で設定してあげましょう。AdHoc配信のために必要なものはここでは説明しません。このあたりの記事を読んでみましょう。

【iOS】複雑な証明書周りをあっさり整理してみた


project.yaml

    settings:

base:
PRODUCT_BUNDLE_IDENTIFIER: "com.skamada.testProject"
CODE_SIGN_STYLE: Manual
configs:
debug:
CODE_SIGN_IDENTITY: iPhone Developer
PROVISIONING_PROFILE_SPECIFIER: Project-profile-debug
DEVELOPMENT_TEAM: # development teamのID
release:
CODE_SIGN_IDENTITY: iPhone Distribution
PROVISIONING_PROFILE_SPECIFIER: Project-profile-release
DEVELOPMENT_TEAM: # development teamのID
scheme:
targets:
testProject



  • settings ここ↓の画面で設定できることをあれこれ設定できます。
    スクリーンショット 2019-08-08 20.23.36.png


    • base: debugとかreleaseとかに共通して設定したいことを記述すると一気に設定してくれます。


    • PRODUCT_BUNDLE_IDENTIFIER 配信時の Bundle IDです。 CFBundleIdentifierと別個に設定しないといけないみたいです。

    • configs: debugとかreleaseとかで分けて設定したい場合は記入しましょう。


    • CODE_SIGN_IDENTITY iPhone Developerとか、iPhone Distributionとかです。


    • PROVISIONING_PROFILE_SPECIFIER プロビの名前を書きましょう。


    • DEVELOPMENT_TEAM Apple DeveloperのチームのIDを記入しましょう。チーム名ではありません。 (ここで数時間ハマりました)


    • scheme Build Scheme↓の設定です。この設定をしないと、Xcodeさんの気分次第でNo Schemeになってしまいます。

      スクリーンショット 2019-08-08 20.31.51.png




.gitignoreに追記する

.xcodeprojInfo.plistはproject.yamlから一意に生成できる

.xcodeprojInfo.plistはわざわざgit共有しなくてもいい


ので、gitignoreに追加してあげましょう。


.gitignore

Info.plist

*.xcworkspace
*.xcodeproj


project.yamlを作るのに苦労したところ

配信設定などは特に、project.yamlを作るのに結構苦労しました。


  • なぜ苦労したか



    • DEVELOPMENT_TEAMはチーム名でなくてチームIDに気づくのに時間がかかった

    • プロジェクトを完成させるのが最優先で、project.yamlを完成させるのは必須ではなかったから

    • 足りない設定は最悪プロジェクト生成後に手作業で生成すればいいから




DEVELOPMENT_TEAMはともかく、モチベーション的な要素が大きかった。

面倒ではあるけど、プロビの設定とかはXcode上でも2クリックくらいで済むので後回しになってました。何より先にちゃんと要件を満たしたコードを書かないと!と思うのが実装者の性。。。?


メリットとデメリットの整理


メリット


.xcprojectファイルがコンフリクトしない

そもそもgit共有しない運用ができるので。


targetの追加が楽

ビルドターゲットの追加はproject.yamlに数行追加するだけで済むので楽になるはずです。大規模になると恩恵が大きそう。


デメリット


ファイルツリーが強制的に同じ位置になる

「自分にとって見やすい配置」とかあると思うんです。でもXcodeGenだとアルファベット順強制です。

ちなみに、XcodeGenを使用するとこの仕様のおかげでファイルツリー構成が同じになるので、.xcodeprojをgitに共有しても.xcprojectコンフリクトはあんまり起こらない(はず)です。1


学習コストがかかる

ツールを導入するので当たり前ですが。設定を1つ追加するたびにREADMEを漁ることになるので学習コスト高めです。


git checkoutのたびにgenerateが必要

ライブラリ構成やファイルツリー構成がproject.yamlに依存するせいか、作業ブランチを変更するたびにgenerateしないとうまく動作しません。iOS開発に慣れている人ほど面倒くさいと感じるはず。


generatepod installはセットでやる

podとxcodeの仕様上仕方ないんだと思いますが、xcodegen generateをするたびにpod installしないと、podで導入したライブラリは認識されません。

即ち、作業ブランチを変える際は

git checkout ~~ -> xcodegen generate -> pod install

の3コマンドが必須になります。

3コマンドとなると結構手間なので、コマンドのaliasを作成するなどして対応するのが吉です。


さいごに:無理して全部のプロジェクトに導入しなくていい。でも1回試してみよう

当然導入するとプロジェクトメンバー全員に対して大きめの学習コストが発生するし、XcodeGen導入によって受けられる恩恵はプロジェクト規模にかなり依存する印象でした(規模大きいほど恩恵大)。試しに使ってみてもいいですがどのプロジェクトに導入するかは考えた方が良さそうです。

とはいえ恩恵はしっかりあるので、せひ一度試してみてください。





  1. .gitignoreで.xcodeprojを共有しないようにするのは私オリジナルの運用方法です。コンフリクトのリスクを最小限に抑えたいならそもそも共有しなければいいじゃん、っていう発想になるよね :thinking: ってことでこの運用にしました。