xcprettyだとXCTestExpectationのタイムアウトエラーやXCTSkipが結果に反映されず、xcbeautifyだと問題なく動く
まだxcprettyを使っていませんか?今こそxcbeautifyに置き換えよう! by ikesyo | プロポーザル | iOSDC Japan 2024 #iosdc - fortee.jp で想定していた内容の一部の供養です(落選)。
社内のとあるプロジェクトで、XCTestExpectationがタイムアウトしているケースでもテストが成功扱いになっていたり、XCTSkipを使ったテストケースがログに出力されていないということがあった。CIでのテスト実行にはfastlaneを使っていたので、fastlaneの実行ログからxcodebuildの出力のフォーマッターを確認してみると、xcprettyが使われていた。
fastlane 2.201.0から scan, gym, snapshot に xcodebuild_formatter
というオプションが増えていて、そのデフォルトは xcbeautify
になっているのだが、これはシステム(PATH?)にxcbeautifyが存在する時だけで、xcbeautifyが存在しない環境ではxcprettyにフォールバックするようになっている。
はてなではiOSアプリのCI/CD環境としてCircleCIを使用している(2024年6月現在)が、CircleCIのMacインスタンスではxcbeautifyはインストールされておらず、xcprettyにフォールバックしていたのだった。
そこでCIのステップとして brew install xcbeautify
を先に実行してxcbeautifyをインストールするようにしたところ、XCTestExpectationのタイムアウトも、XCTSkipも正常に拾われるようになってめでたしめでたし。
xcbeautifyがREADMEで次のように謳っているとおり、
- [x] Supports the new build system's output.
- [x] Supports Xcode's parallel testing output.
- [x] Supports formatting Swift Package Manager output.
現代のXcodeやSwiftPM相手にはxcbeautifyを使っていくべきだということを実感した。
ちなみにGitHub ActionsのMacインスタンスについては macos-12
以降のイメージではxcbeautifyがプリインストールされているので安心していい。
その他のCIプロバイダー、例えばBitriseやCodemagicでのxcbeautifyのインストール状況を知っている方がいたらぜひ教えてください。
追記
- Bitrise: https://stacks.bitrise.io/stack_reports/
- xcbeautifyある 🙆
- Codemagic: https://docs.codemagic.io/specs/versions-macos/
- xcbeautifyない 🙅
UISearchBarのデフォルトの背景色は `UIColor.tertiarySystemFill`
Use system fill colors for items situated on top of an existing background color. System fill colors incorporate transparency to allow the background color to show through.
Use this color to fill large shapes, such as input fields, search bars, or buttons.
と説明されているとおりだった。
ライトでの cgColor
の出力はこんな感じ:
<CGColor 0x60000260e1c0> [<CGColorSpace 0x60000260cf00> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; sRGB IEC61966-2.1; extended range)] ( 0.462745 0.462745 0.501961 0.12 )
UIButton.Configuration.Sizeの各サイズごとのデフォルト状態での高さ
iOS 16.1シミュレーターで確認してみた。
@2x | @3x | |
---|---|---|
UIButton.Configuration.Size.large |
50.5 | 50.33… |
UIButton.Configuration.Size.medium |
34.5 | 34.33… |
UIButton.Configuration.Size.small |
28 | 28 |
UIButton.Configuration.Size.mini |
28 | 28 |
特別にHuman Interface Guidelinesの44x44ポイントのヒットターゲットが意識されている・遵守するようになっているわけではないのか。
Buttons - Menus and actions - Components - Human Interface Guidelines - Design - Apple Developer
Make buttons easy for people to choose. On a touchscreen, buttons need a hit target of at least 44x44 points to accommodate a fingertip.
iOSシミュレーター・Androidエミュレーターにアプリをインストールだけする(起動までしない)方法
なぜ
アプリの初回起動時専用の特別な処理や画面遷移を行うケースで、そのデバッグ実行・動作確認をするため。
iOS
まずインストールするためのアプリは Build For Running
でビルドしておく。次にシミュレーターを起動しておいて
xcrun simctl install booted <~~.appのパス>
のコマンドを実行する。
もしくは、シミュレーターのウィンドウに ~~.app
をFinderからドラッグ&ドロップする。
Xcodeのスキーム設定の Wait for the executable to be launched
が使えるのでは、と一瞬考える。しかしこれはシミュレーターにインストール済みのアプリには使えるのだが、まだインストールされていないアプリの場合は、アプリのインストールまではやってくれないので、ただ待つだけ(何も起きない)状態になる。ので、別途自分でインストールしてあげる必要がある。
UserDefaultsやKeychainも含めてまっさらな初回起動状態の再現のために、シミュレーターのメニューの Device
-> Erase All Content and Settings…
も便利。
Android
エミュレーターを起動しておいて
gradlew installDevelopDebug
のコマンドを実行する。DevelopDebug
の部分は使いたいbuild variantを指定する。こちらはエミュレーターではなく実機接続でも動作する。
Mockolo 1.7.0のビルド済みバイナリがarm64にしか対応していない
https://github.com/uber/mockolo/releases/download/1.7.0/mockolo.tar.gz に含まれる mockolo
バイナリがarm64アーキテクチャーにしか対応していなかった。これだとAppleシリコンMacでは実行できるが、Intel Macでは実行できない。
$ file mockolo mockolo: Mach-O 64-bit executable arm64
その前はどうかと思って1.6.3を確認してみたら、そちらはx86_64だけだった。これは本当はx86_64でもarm64でも実行できるユニバーサルバイナリになっていてほしい。
ということで修正PRを出してみている。SwiftPMでは swift build
の引数に --arch arm64 --arch x86_64
を足すだけでユニバーサルバイナリにできるので、それをやっただけです。
追記
このPRを含む1.7.1がリリースされていました。Intel Macの方もAppleシリコンMacの方もどうぞご利用ください。
FirebaseのSwiftPM対応に伴ってGoogle Tag ManagerもSwiftPMでインストールできるようになっていた
iOS(ないしAppleプラットフォーム全般)のFirebaseといえばこれまでインストール方法はCocoaPodsが基本だったが、バージョン8.0.0からSwiftPMでもインストールできるようになっている。
- https://firebase.google.com/docs/ios/installation-methods#swift-package-manager
- firebase-ios-sdk/SwiftPackageManager.md at master · firebase/firebase-ios-sdk · GitHub
さて、Firebase Analyticsと組み合わせて使えるツールとしてアプリ向けのGoogle Tag Manager(以下GTM)もあるのだが、これもSwiftPMで扱えるのか。8.0.0がリリースされた直後の2021年5月時点ではサポートされていなかったのだが、2021年8月からひっそりとサポートされるようになっていた。
Please check the GTM doc, Google Tag Manager supports Swift Package Manager now. Please make sure to add -ObjC linker flag.
— https://github.com/firebase/firebase-ios-sdk/issues/6663#issuecomment-908755918
- https://developers.google.com/tag-platform/tag-manager/ios/v5#swift-package-manager
ということでGTMのSwift Packageは https://github.com/googleanalytics/google-tag-manager-ios-sdk で公開されているので、これを使えば良いことがわかった。
ところでGTMのSwift Packageはバイナリとしては https://dl.google.com/firebase/ios/tagmanager/swiftpm/7.4.0/GoogleTagManager.zip というzipを参照していて、これはCocoaPods向けの https://dl.google.com/firebase/ios/tagmanager/405c0e6ab8f653b7/GoogleTagManager-7.4.0.tar.gz とは別物になっている。中身を確認してみたところ、CocoaPods向けバイナリ(xcframework)では TagManagerResources.bundle
というバンドルがxcframeworkのResourcesに含まれていて、podspecでもそのバンドルがアプリに追加されるようになっている。
TagManagerResources.bundle
の中身は Info.plist
を除くと TAGRuntime.js.dat
というファイルが1つだけだった。SwiftPM向けのxcframeworkを確認してみると、そこにはResourcesも TagManagerResources.bundle
も TAGRuntime.js.dat
も見当たらない。これはちゃんと動くのだろうかと不安になったが、Mach-O Executables · objc.io に書かれている手段で、otool
を使って中身をチェックしてみた。
$ xcrun otool -v -s __TEXT __cstring -arch arm64 GoogleTagManager.xcframework/ios-arm64_armv7/GoogleTagManager.framework/GoogleTagManager | grep TAGRuntime 0000000000001857 T@"TAGRuntime",&,N,V_runtime GoogleTagManager.xcframework/ios-arm64_armv7/GoogleTagManager.framework/GoogleTagManager(TAGRuntime+Script.o): GoogleTagManager.xcframework/ios-arm64_armv7/GoogleTagManager.framework/GoogleTagManager(TAGRuntime.o):
TAGRuntime+Script.o
というのがいかにも怪しい(それっぽい)のでセクションの中身も見てみる。
$ xcrun otool -v -s __TEXT __cstring -arch arm64 GoogleTagManager.xcframework/ios-arm64_armv7/GoogleTagManager.framework/GoogleTagManager | grep TAGRuntime+Script -A2 GoogleTagManager.xcframework/ios-arm64_armv7/GoogleTagManager.framework/GoogleTagManager(TAGRuntime+Script.o): Contents of (__TEXT,__cstring) section 000000000000000c \nvar aa,ba=this,h=function(a,b){var c=a.split("."),d=ba;c[0]in d||!d.execScript||d.execScript...
ということで、いかにもJavaScriptらしきものが確認できて、この内容は TAGRuntime.js.dat
と合致していた。SwiftPM向けのバイナリではスクリプトがコード中に埋め込まれていたのですね。めでたしめでたし。
App Storeの定期購読・無料トライアルの基準タイムゾーンはUTC
App Store Connectのドキュメント(ヘルプページ)にはこうあります:
自動更新登録は、1 か月間の日数ごとではなく、最初の購入の暦日と同じ日に更新します。たとえば、カスタマーが 1 か月の無料トライアルを 1 月 7 日に開始する場合、トライアルが終了するのは 2 月 7 日です。翌月に同じ日が存在しない日に 1 か月のトライアルを開始した場合、トライアルが終了するのは翌月の末日ですが、利用可能になると元の日付に戻ります。たとえば、カスタマーが 1 月 30 日に登録すると、次の更新日は、2 月 28 日 (閏年の場合は 2 月 29 日) で、その次は 3 月 30 日になります。
これだけだと「そうですね」となるのですが、仕事で見かけた事例によると、どうも日時はUTCのタイムゾーンを基準としているようです。日本時間などユーザーのローカルタイムゾーン基準でないことで、次のようなややこしいことが発生し得ます。
- 日本時間の3月1日 午前9時(UTC 3月1日 午前0時)に無料トライアルを開始
- 無料トライアルの終了日(課金開始日)は4月1日
- 日本時間の3月1日 午前8時(UTC 2月28日 午後11時)に無料トライアルを開始
- 無料トライアルの終了日(課金開始日)は3月28日で、4月1日ではない!
日本時間では同じ3月1日に無料トライアルを開始しても、たった1時間の違いで無料トライアルの期間が3日間も短くなってしまいます。
ということで、開発者としてユーザーからの問い合わせへの返答にも、ユーザーとして無料トライアルを最大限活用するのにも使える豆知識でした。