ライブラリ管理を CocoaPods から Swift Package Manager(Xcode)へ移行する

以下のライブラリを CocoaPods で管理していたが、Swift Package Manager(Xcode)管理に移行してみる。

  • SwiftLint
  • LicensePlist
  • Firebase
    • AnalyticsWithoutAdIdSupport
    • Crashlytics

以下が、その手順。

CocoaPods でのライブラリ管理を削除

まず CocoaPods でのライブラリ管理をやめる。

以下を実行して、*.pbxproj から Pods_XXX.framework への参照と Pods フォルダを削除する。

pod deintegrate

上記コマンドでは消えない CocoaPods 関連のファイルを、手動で削除する。

  • *.xcworkspace
  • Podfile
  • Podfile.lock

※必要に応じて .gitignore からも CocoaPods 関連ファイルの記述を削除する(たとえば、 Pods フォルダとか)

Xcode*.xcodeproj を開く。
Xcode 左側の Project Navigator に Pods フォルダの参照が残っている場合は、削除する。

BuildPhase にある Pods を利用した SwiftLint, LisencePlist, Crashlytics のスクリプトを削除する。

Swift Package Manager でライブラリを追加

Package dependencies から + ボタンをクリックしてライブラリを追加する。

SwiftLint

公式ドキュメントのこのあたりが参考になる。

パッケージ追加の画面で以下を検索して、Add Package する。
https://github.com/realm/SwiftLint

Package Product をターゲット追加する画面が表示されるので、すべて「None」を選び、ターゲット追加しない。Add Package を押す。

Xcode プロジェクトに Package が追加されたことが確認できる。

次に、Lint が実行されるように、Build Tool Plug-ins を設定する。
Lint 対象のターゲットを選び、Build Phases から Run Build Tool Plug-ins の項目を開き、+ボタンを押す。

SwiftLintPlugin を選び、Add する。

SwiftLintPlugin が追加されたことが確認できる。

この状態で、ビルドすると SwiftLint が実行される。
プロジェクトのルートフォルダに .swiftlint.yml がある場合は、そこから Lint の設定が読み込まれる。

LicensePlist

公式ドキュメントのこのあたりが参考になる。

パッケージ追加の画面で以下を検索して、Add Package する。
https://github.com/mono0926/LicensePlist

Package Product をターゲット追加する画面が表示されるので、すべて「None」を選び、ターゲット追加しない。Add Package を押す。

Xcode プロジェクトに Package が追加されたことが確認できる。

LicensePlist のファイルが生成されるように、Build Tool Plug-ins を設定する。
対象のターゲットを選び、Build Phases から Run Build Tool Plug-ins の項目を開き、+ボタンを押す。

LicensePlistBuildTool を選び、Add する。

LicensePlistBuildTool が追加されたことが確認できる。

プロジェクトのルートフォルダに license_plist.yml を配置する。
最低限、以下の記述を追加する。

options:
  xcodeprojPath: "*.xcodeproj"  # 対象のプロジェクトファイルのパス
  gitHubToken: ghp_XXX  # GitHub トークン(LicensePlist は GitHub API を使うので、その制限エラーを避けるため)

Settings.bundle に LicensePlist の成果物をコピーする時には、公式ドキュメントにある以下のスクリプトリンク)を BuildPhases に追加する。

echo "Will copy acknowledgements"
ACKNOWLEDGEMENTS_DIR=${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/com.mono0926.LicensePlist.Output
DESTINATION_PATH=${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Settings.bundle/
cp -r "${ACKNOWLEDGEMENTS_DIR}"/* "${DESTINATION_PATH}"
rm -rf "${ACKNOWLEDGEMENTS_DIR}"

私の環境では、プロジェクト内にある Settings.bundle に成果物をコピーしたかったので、DESTINATION_PATH を変えて以下のようにした。

echo "Will copy acknowledgements"
ACKNOWLEDGEMENTS_DIR=${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/com.mono0926.LicensePlist.Output
DESTINATION_PATH=${PRODUCT_NAME}/Settings.bundle/
cp -r "${ACKNOWLEDGEMENTS_DIR}"/* "${DESTINATION_PATH}"
rm -rf "${ACKNOWLEDGEMENTS_DIR}"

Firebase

公式ドキュメントに Swift Package Manager の導入方法があるので、それに沿って導入できる。

パッケージ追加の画面で以下を検索して、Add Package する。
https://github.com/firebase/firebase-ios-sdk.git

Package Product をターゲット追加する画面が表示されるので、使用するプロダクトを選んで、Add Package する。
今回は、PodsAnalyticsWithoutAdIdSupport, Crashlytics を入れていたので、それらにターゲットを設定する。

Xcode プロジェクトに Package が追加されたことが確認できる。

Crashlytics を使用する場合、ビルド後にデバッグシンボルをアップロードするシェル(リンク)を BuildPhases に追加する。

${BUILD_DIR%Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run

Firebase が正しく導入されたかをチェックするために、-FIRAnalyticsDebugEnabled を引数に追加してアプリを起動し、イベントを送信する。(最初のハイフンも含めることに注意)
Firebase Console の Debug View でイベントが確認できたら OK.
参考

iOS アプリで1画面だけ色の表示が違った件

具体的には LaunchScreen.storyboard に設定している色が、 Hex Color は同じだが、微妙に他の画面と表示が違う。

原因は Color Profile が「Generic RGB」になっていたので「sRGB」に変更したら解決。

現在の XcodeXcode 15)で storyboard 作ると sRGB でできるが(SwiftUI の Color もデフォルト sRGB)、結構古い Xcode で作ってある storyboard は Generic RGB のままなので注意が必要。

storyboard の Color Profile を確認・変更するには、カラーピッカーの 「...」となっているボタンを押すと

Color Profile のリストが表示されるので、そこから変更できる。(Color profile を変更すると、設定している色のコードも変わるので、また色のコードを設定しなおす必要がある)

Xcode 14.3 で CocoaPods を使用しているプロジェクトのアーカイブが失敗する

Xcode 14.3 から CocoaPods を使用しているプロジェクトで、Archive を実行すると、以下のような rsync error... になって、アーカイブが作成されない。

rsync error: some files could not be transferred (code 23) at /AppleInternal/Library/BuildRoots/xxxxxxxx-yyyy-zzz-aaaa-bbbbbbbbbbbb/Library/Caches/com.apple.xbs/Sources/rsync/rsync/main.c(996) [sender=2.6.9]

対処法は以下。

まず、使用している CocoaPods のバージョンを v1.12.1 以降にする。 (たとえば brew で pod インストールしているならば、 brew upgrade cocoapods で更新できる)

更新後、 pod --version でバージョンが更新されていれば OK。

pod install する。

pod install 後に、 Pods フォルダ以下にできている Pods-XXX-frameworks.sh の中が

  if [ -L "${source}" ]; then
    echo "Symlinked..."
    source="$(readlink -f "${source}")"
  fi

↑のように readlink コマンドに -f オプションが付くようになっていれば、OK。

この状態で、アーカイブが成功するようになる。

NHK for School API を使った Flutter の習作

Flutter を作って何かアプリを作ってみようと思い、
NHK for School API という API を使って、アプリを作ってみた。

github.com

NHK for School API を使うと NHK for School 公開されているコンテンツを API で取得できる。
(すべてが取得できるわけでなく、現在だと小・中学校の理科と社会科のコンテンツだけのようだ)

www.nhk.or.jp

このアプリ作成を通じて Flutter アプリでの以下の実現方法を学べた。

  • HTTP によるデータ取得
  • アプリ内データ保存(shared_preferences)
  • Widget の使い方(レイアウトの組み方など)

情報提供 NHK

東京ガスの「ずっとも電気1」と「基本プラン」の電力量料金の計算

東京ガス

の「電力量料金」(電力を使用した分の電気代)を計算できる Web ツールを作った。

電力量料金の計算

  • 基本料金、燃料費調整額、再エネ促進賦課金、ガス・電気セット割については計算に含まれていません。
  • ※計算結果が正しいことは保証しませんので、ご注意ください。

「段階の区切りになる使用量」、「段階ごとの 1kWh単位の料金」を変更すると、東京ガス以外にも使用できると思います。

Swift の未使用コードを分析する periphery コマンドのオプションメモ

periphery を使用すると Swift プロジェクトの中で未使用なコードを特定できる。
(未使用なクラス・プロパティ、不要な public がわかる)

インストールして、コマンドのオプションを調べたのでメモする。

インストール

brew 経由だと

brew install peripher

ヘルプを実行

periphery scan --help 

実行結果。オプションが羅列される。

% periphery help scan   
OVERVIEW: Scan for unused code

USAGE: periphery scan [<options>] [<build-arguments> ...]

ARGUMENTS:
  <build-arguments>       Arguments following '--' will be passed to the underlying build tool, which is either 'swift build' or
                          'xcodebuild' depending on your project

OPTIONS:
  --setup                 Enable guided setup
  --config <config>       Path to configuration file. By default Periphery will look for .periphery.yml in the current directory
  --workspace <workspace> Path to your project's .xcworkspace. Xcode projects only
  --project <project>     Path to your project's .xcodeproj - supply this option if your project doesn't have an .xcworkspace. Xcode
                          projects only
  --schemes <schemes>     Comma-separated list of schemes that must be built in order to produce the targets passed to the --targets
                          option. Xcode projects only (default: [])
  --targets <targets>     Comma-separated list of target names to scan. Required for Xcode projects. Optional for Swift Package Manager
                          projects, default behavior is to scan all targets defined in Package.swift (default: [])
  --format <format>       Output format (allowed: xcode, csv, json, checkstyle) (default: xcode)
  --index-exclude <index-exclude>
                          Path glob of source files which should be excluded from indexing. Declarations and references within these
                          files will not be considered during analysis. Multiple globs may be delimited by a pipe (default: [])
  --report-exclude <report-exclude>
                          Path glob of source files which should be excluded from the results. Note that this option is purely
                          cosmetic, these files will still be indexed. Multiple globs may be delimited by a pipe (default: [])
  --index-store-path <index-store-path>
                          Path to index store to use. Implies '--skip-build'
  --retain-public         Retain all public declarations - you'll likely want to enable this if you're scanning a framework/library
                          project
  --disable-redundant-public-analysis
                          Disable identification of redundant public accessibility
  --retain-assign-only-properties
                          Retain properties that are assigned, but never used
  --retain-assign-only-property-types <retain-assign-only-property-types>
                          Comma-separated list of property types to retain if the property is assigned, but never read (default: [])
  --external-encodable-protocols <external-encodable-protocols>
                          Comma-separated list of external protocols that inherit Encodable. Properties of types conforming to these
                          protocols will be retained (default: [])
  --retain-objc-accessible
                          Retain declarations that are exposed to Objective-C implicitly by inheriting NSObject classes, or explicitly
                          with the @objc and @objcMembers attributes
  --retain-unused-protocol-func-params
                          Retain unused protocol function parameters, even if the parameter is unused in all conforming functions
  --clean-build           Clean existing build artifacts before building
  --skip-build            Skip the project build step
  --strict                Exit with non-zero status if any unused code is found
  --disable-update-check  Disable checking for updates
  --verbose               Enable verbose logging
  --quiet                 Only output results
  -h, --help              Show help information.

オプションのメモ

--setup

対話形式でセットアップして分析する。
セットアップした結果は .periphery.yml に構成が保存されるので次回以降はセットアップせずに yml から構成を指定して、分析できる。

--config

.periphery.yml のパスを指定する。
デフォルトはカレントディレクトリ。

--workspace

Xcode ワークスペースへのパス。

--project

Xcode プロジェクトへのパス。
ワークスペースがない場合は、こちらを指定する。

--schemes

対象スキーム。
カンマ区切りで複数指定できる。
単体指定も可能。

--targets

対象ターゲット。
カンマ区切りで複数指定できる。
単体指定も可能。

--format

出力フォーマット。以下が指定可能。

デフォルトは xcode
Xcode で Build Phase に追加して、Xcode のエディタ上で結果を表示したい時は xcode 指定にする。
表として、出力したい時は csv が便利。

--index-exclude

インデックス作成から除外するパス。

--report-exclude

出力される結果から除外するパス。
インデックス作成自体はされることに注意。

--index-store-path

インデックスストアのパスを指定する。
この場合、プロジェクトのビルドをスキップする。
--skip-build と同様に)

--retain-public

public された宣言はすべて外部から使用される想定にする。
フレームワークやライブラリのプロジェクトで、実際にそのインターフェースを使用していないプロジェクトの場合にこれを指定する。

--disable-redundant-public-analysis

冗長なパブリックアクセシビリティの識別を無効にする。

--retain-assign-only-propertie

割り当てられているが使用されていないプロパティを保持する。

--retain-assign-only-property-types

プロパティが割り当てられているが、読み取られない場合に保持するプロパティタイプのコンマ区切りリスト。

--external-encodable-protocols

Encodableを継承する外部プロトコルのコンマ区切りリスト。 これらに準拠するタイプのプロパティプロトコルは保持されます。

--retain-objc-accessible

NSObjectクラスを継承することによって暗黙的に、または@objc属性と@objcMembers属性を使用して明示的に、Objective-Cに公開される宣言を保持します。

--retain-unused-protocol-func-params

パラメータがすべての準拠関数で使用されていない場合でも、未使用のプロトコル関数パラメータを保持します。

--clean-build

既存のビルドをクリアしてからビルドする。

誤った結果が出ている時は、インデックスストアが破損したり・ソースファイルと同期しなくなった可能性がある。
たとえば、スキャンを強制的に終了(^C)した場合にこれが発生することがある。
この時は --clean-build すると良い。

--skip-build

ビルドをスキップする。

--strict

未使用コードが見つかった場合は非ゼロのステータスで終了する。

--disable-update-check

更新のチェックを無効にする。

--verbose

冗長なログを出力。

これを指定すると、

[configuration:begin]

から始まる箇所に、分析の構成が表示されるので、それを .periphery.yml に貼り付けると、構成を永続化できる。

--quiet

結果のみを出力。

-h, --help

ヘルプを表示。

CI で使用する場合

https://github.com/peripheryapp/periphery#continuous-integration

たとえばテストを実行した後に periphery を実行する時は

--skip-build でビルドをスキップできる。

インデックスパスが非標準の場所にあるときは

--index-store-path で指定する。

たとえば以下のスキームがあって

  • A
  • B
  • C

それぞれ以下のターゲットがあるとき

  • A
  • B
  • C

これらをそれぞれ分析して、結果を CSV に出力する時は以下のシェルになる。

array=(A B C)
for i in "${array[@]}"
do
    periphery scan --project test.xcodeproj --schemes ${i} --targets ${i} --format csv > ~/Desktop/test/periphery/${i}.csv
done