potatotips #77で「RenovateによるiOSライブラリーの自動更新」という発表をしました&いくつかの補足

発表資料はこちらです 👇

発表の中では時間の都合で触れられなかったところをいくつか補足しようと思います。

CocoaPodsでRealmを使っているとPodfile.lockが更新できない件

github.com

Renovateの実行環境はDockerコンテナの中のUbuntuです。Renovateは Podfile を書き換えた後に pod install を実行してPodfile.lock を更新します。CocoaPodsはLinuxで動くのか?と思われるかもしれませんが、pod gem(や依存の xcodeproj)もLinuxで問題なく動作するようです。ほとんどのライブラリーは問題ないのですが、Realmのpodspecには少し特殊なところがあります。

https://github.com/realm/realm-swift/blob/8c1cbf124c5025da9220595e7764059963065891/Realm.podspec#L110

  s.prepare_command         = 'sh scripts/setup-cocoapods.sh'

podspecのprepare_commandスクリプトを実行しています。そのスクリプトではrealm-coreをダウンロードしているのですが、その先頭でset -euo pipefailとしており、bashではなくshだとエラーになってしまいます。

build.sh: 13: set: Illegal option -o pipefail

さらにここをbashで実行したとしてもスクリプト内部でxcode-selectコマンドが使われているので、どちらにせよ詰んでしまいます。

ここで我々ができることは、発表のSwiftPMの箇所で「Package.resolvedの更新に非対応」でも触れた

  • PRに自分で追いコミットをするか、もしくは
  • CIで(例えばGitHub Actionsで)更新するワークフローを用意する

という感じになりそうです。

on:
  pull_request:
    paths:
      - Podfile
jobs:
  precheck:
    runs-on: ubuntu-latest
    outputs:
      files_changed: ${{ steps.file_changes.outputs.files }}
    steps:
      - uses: actions/checkout@v3
      - id: file_changes
        uses: trilom/file-changes-action@v1.2.4
  update:
    runs-on: macos-latest
    needs: precheck
    if: contains(fromJson(needs.precheck.outputs.files_changed), 'Podfile.lock') == false
    steps:
      - ...

CocoaPodsとXcodeGenを併用しているとPodfile.lockが更新できない件

XcodeGenを使用していると、通常生成したXcodeプロジェクトはリポジトリにコミットせず、CIでも初期セットアップでコマンドを実行してXcodeプロジェクトを生成すると思います。これがRenovateでは困ったことになります。Renovateの実行時に任意の初期化処理を挟むことができず(そもそも実行環境にXcodeGenも存在しない)、Xcodeプロジェクトが存在しないままだとpod installの実行に失敗してしまうためです。

これを回避する手段として、CocoaPodsの:integrate_targetsという設定を使用できます。Podfileのルートではinstall!というオプションを設定することができます。その引数に :integrate_targets => false と指定することで、Xcodeプロジェクトへの組み込みステップが省略されるため、Xcodeプロジェクトがなくても pod install を実行できるようになります。

この設定をRenovateでの実行時だけ有効にするように、以下のようにハックします。

is_running_in_renovate = ENV['HOME'] == '/home/ubuntu'
if is_running_in_renovate
  # Renovateでの実行時にはXcodeGenが実行できず、xcodeprojが存在しないので `:integrate_targets => false` とする。
  install! 'cocoapods', :integrate_targets => false
else
  install! 'cocoapods'
end

ただこの設定には注意点があり、:integrate_targets => falseだとアプリが使用しているSwiftのバージョンをXcodeプロジェクトから抜き出すことできなくなり、ライブラリー側のビルド設定のSWIFT_VERSIONが設定されなくなってしまいます。

これも回避策があり、Podfile内の各ターゲットで current_target_definition.swift_version = '5.0' と明示的に定義してしまえば問題ありません。

target 'FooBarApp' do
  # https://github.com/CocoaPods/CocoaPods/issues/8653#issuecomment-488767262
  current_target_definition.swift_version = '5.0'
  ...
end

おわりに

モバイルアプリ開発でもどんどんRenovateを活用していきたいですね!!

その他の方の発表資料はconnpassのページからどうぞ!

CocoaPods 1.7.0からSpecsのソースにCDNを使えるようになります

先日リリースされたCocoaPods 1.7.0.beta.1で、実験的機能としてSpecsのソース(パッケージ定義であるpodspecファイルのセントラルリポジトリ)にCDNを利用できるようになりました。

今までは、ソースとして https://github.com/CocoaPods/Specs のGitリポジトリが使用されており、初期セットアップ時のクローンや、日々のアップデートに結構な時間が掛かっていましたが、ソースのCDN化によりこうした時間が削減できるようになります(必要なデータだけダウンロードされてローカルにキャッシュもされる)。

また、ライブラリの最新バージョンを使おうとしたらローカルのSpecsが更新されていなくて、その都度$ pod repo update(もしくは$ pod install --repo-updateのオプション付き実行)をしないといけない、というのもなくなるはずです。

Beta期間中の結果によっては、1.7.0の正式リリース時点でCDNソースがデフォルトになるようです。

Depending on the results and stability, we hope that starting with 1.7.0, CocoaPods will no longer require users to clone the master specs repo in order to get started.

この機能を有効にするには、1.7.0.beta.1にアップデートし、プロジェクトのPodfileの先頭に次の1行を足すだけです。

# source 'https://github.com/CocoaPods/Specs' 既存のこの行があれば削除かコメントアウトしましょう
source 'https://cdn.jsdelivr.net/cocoa/'

ということでどんどん試していきましょう!💪

CocoaPodsにtest_specというのがあるのを今更ながら知った

CocoaPods 1.6.0 Betaのブログ記事(2018年8月)を今更ながら読んでいたら、s.test_specという定義ができるのを遅まきながら知った。

Pod::Spec.new do |s|
  # ... rest of root spec entries go here

  # Unit Test Sources - Those do not require an app host to run. 
  # They also require 'OCMock' dependency.
  s.test_spec 'Tests' do |test_spec|
    test_spec.source_files = 'Tests/**/*.{h,m}'
    test_spec.dependency 'OCMock'
  end

  # SnapShot Tests Sources - Those *do* require an app host to run.
  s.test_spec 'SnapshotTests' do |test_spec|
    test_spec.requires_app_host = true
    test_spec.source_files = 'SnapshotTests/**/*.{h,m}'
  end

おまけで、cocoapods-generateという、podspecからXcodeのプロジェクトを生成してくれるプラグインも紹介されていてよさそうだった。SwiftPM(Swift Package Manager)の$ swift package generate-xcodeprojと一緒の感じですね。

Xcodeのプロジェクト生成だとXcodeGenという、YamlJSONの定義から生成するツールもある。

既存iOSアプリへのReact Native導入とcocoapods-fix-react-native

  • 既存iOSアプリへのReact Native導入は公式にドキュメントが用意されている
  • その手順に従うと、React Nativeの依存解決にはCocoaPodsを使うようになっている
    • 一方 react-native init で新規にプロジェクトを作成する場合は、各ライブラリがstatic libraryとしてxcodeprojに直接設定されるようになっている
  • React NativeのCocoaPodsサポートではsubspecが使われているが、subspecではヘッダーのディレクトリが分割されないので、static libraryとして組み込む場合とは一部ヘッダーのインポート方法が異なってビルドエラーになる
    • 他にもエラーになる要因はいくつかある
  • そのような問題を解消してくれるのが https://github.com/orta/cocoapods-fix-react-native/ というCocoaPods用のプラグイン

ということで、既存iOSアプリにReact Nativeを導入したい場合はcocoapods-fix-react-nativeプラグインを使っていきましょう 💪

関西モバイルアプリ研究会 #4 でLTしてきました #関モバ

第1回から参加させていただいている関西モバイルアプリ研究会 #4でLTをしてきました。なんだかんだでGoogle I/OWWDC 特別会だった#3以外の3回全てで発表できていて嬉しい限りです! 💖

以下が発表スライドです。

OS Xでは以前から使えたものの、iOSではiOS 8からやっと使えるようになったDynamic FrameworkをどうやってXcodeプロジェクトに導入するのか、という選択肢・手順についてまとめてみました。

またiOS 7ではDynamic Frameworkが使えず、そのままではSwift製のライブラリを導入するのが結構辛いのですが、その辺を簡単に導入してくれるCocoaSeedsというツールについても少しだけ触れてみました(まだあんまり知られてない)。

発表後にFrameworkのコード署名周りの話を @griffin_stewie さんとしていたんですが、Frameworkをアプリターゲットに導入する際に“Embed Frameworks”ビルドフェーズの各リスト項目で“Code Sign On Copy”というチェックボックスをオンにしておけば、アプリ用のコード署名設定でFrameworkが再署名されるので、Framework自体のコード署名はiPhone Developerの適当なものでも構わない、という知見を共有することもできたので、発表した甲斐がありました! 😄

次回の関モバ #5は2015/8/26(水)開催とのことでした 🚀

その他皆さんの発表資料などは以下からどうぞ!

kanmoba.connpass.com

itok.jp

usami-k.hatenablog.com

yashigani.hatenablog.com

ACAccountStoreのReactiveCocoaラッパーを作った

タイトルのままですが ikesyo/ReactiveAccountStore というAccounts.framework (ACAccountStore) のReactiveCocoaラッパーを作ってみました。

現時点では、以下の4つの非同期メソッドのRACSignalラップ版をサポートしています。

  • -requestAccessToAccountsWithType:options:completion:
  • -saveAccount:withCompletionHandler:
  • -renewCredentialsForAccount:completion:
  • -removeAccount:withCompletionHandler:

より詳しくはヘッダードキュメントを参照してください。

すでにCocoaPodsに登録済みなので、以下で利用できるようになります。

pod 'ReactiveAccountStore'

対応プラットフォームは、OS Xのサポートとdeprecatedのメソッドの関係上、

となっています。

Accounts.frameworkを使用することがあるReactiveCocoaユーザーは利用してみてください(どれだけいるのか)。

VTAcknowledgementsViewControllerのStoryboardサポートを改善した

Pull requestしたらローカライズ依頼された - [yashigani days] などで知ったVTAcknowledgementsViewControllerというライブラリがあります。CocoaPodsが自動生成してくれる各ライブラリのライセンス表記をまとめたファイルをうまいことアプリ内で画面表示してくれる便利ライブラリです。

CocoaPodsが生成するファイル名は通常、Pods-acknowledgements.plistですが、Podfileで下記のようにターゲットを明示的に指定している場合、

# Podfile
target "TargetName" do
  pod 'VTAcknowledgementsViewController'
end

生成されるファイル名がPods-TargetName-acknowledgements.plistというように変わります。

ライブラリの機能としてコードベースでの表示では、以下のようにしてターゲット入りのカスタムPlistファイル名に対応していたのですが、

NSString *path = [[NSBundle mainBundle] pathForResource:@"Pods-TargetName-acknowledgements" ofType:@"plist"];
VTAcknowledgementsViewController *viewController = [[VTAcknowledgementsViewController alloc] initWithAcknowledgementsPlistPath:path];

元のエントリーで追加されていたStoryboardサポートではカスタムPlistファイル名は未対応でした。ということで、Storyboardでの配置でもカスタムPlistファイル名に対応するPull requestをしたらマージされましたよ、というお話です。

Support custom plist name for Storyboard by ikesyo · Pull Request #21 · vtourraine/VTAcknowledgementsViewController · GitHub

設定方法

  1. 元エントリーの画像のように、Storyboard上にUITableViewControllerを配置し、Custom ClassにVTAcknowledgementsViewControllerを指定する。
  2. 配置したTableViewControllerのUser Defined Runtime Attributes
    • Key Path: acknowledgementsPlistName
    • Type: String
    • Value: ファイル名(上記例では "Pods-TargetName-acknowledgements")

    としたものを追加する。

実装内容

設定方法の通り、対応にはUser Defined Runtime Attributesを使用しています。User Defined Runtime Attributesでセットされた値が実際にプロパティにセットされるのは、initWithCoder:awakeFromNibの間のため、Storyboardベースでの初期化のエントリーポイントをinitWithCoder:からawakeFromNibに変更した上で、プロパティに値がセットされているかどうかでデフォルトのファイル名を使用するか、カスタムファイル名を使用するかどうかを切り分けるようにしています。

注意

2014/04/15現在、CocoaPodsでのリリースには上記対応はまだ含まれていません。CHANGELOGを見る限り、次のリリースとなる ver 0.11 で入るのでリリースを待つか、

# Podfile
pod 'VTAcknowledgementsViewController', :head

Podfileの:headオプションでリポジトリからmasterブランチを直接取得してくれば使えるようになります。

それでは便利なCocoaPodsライフを!

2014/05/05 追記

ver 0.11 がリリースされました!ので、以下だけで本対応が含まれたバージョンが使用できます。

# Podfile
pod 'VTAcknowledgementsViewController'