自作パッケージをCocoaPodsに追加する方法

「Swift Packagesによるライブラリの作成方法」は4部構成です。
記事を順番に読み進めると、Swift Packagesでライブラリを自作して公開できるようになります。

- 第1部: [Swift Packagesでライブラリを自作する](https://qiita.com/uhooi/items/2f36b85f5f41cbd35189)
- 第2部: [自作パッケージをSwift Package Indexに追加する](https://qiita.com/uhooi/items/689801dad2cf2e0a37b4)
- 第3部: 自作パッケージをCocoaPodsに追加する　__←イマココ__
- 第4部: [自作パッケージをCarthageに対応する](https://qiita.com/uhooi/items/ada0a02737ecb2d1c8d7)

## はじめに

自作パッケージをCocoaPodsに追加する方法を紹介します。

## 環境

- OS： macOS Big Sur 11.4
- Xcode：12.4 (12D4e)
- Swift：5.3.2
- swift-tools：5.3

## CocoaPodsへの追加方法

CocoaPodsへは `pod` コマンドを使ってデプロイするので、その手順を紹介します。

### パッケージを自作する

自作パッケージがなければ始まりません。

以下の記事を参考にパッケージを自作します。

https://qiita.com/uhooi/items/2f36b85f5f41cbd35189

### CocoaPodsのインストール

以下の記事などを参考にCocoaPodsをインストールします。

https://qiita.com/uhooi/items/1134b0f784028412cb83

私は上記の記事を参考にせず、 [Bundler](https://qiita.com/uhooi/items/4abf8c282ae23a259e4f) を使ってCocoaPodsをインストールしました。

### CocoaPodsプロジェクトの生成

CocoaPodsをインストールしたら、 `pod lib create {ライブラリ名}` を実行してプロジェクトを生成します。

私はBundlerを使っているので先頭に `bundle exec` が付いています。

```shell-session
$ bundle exec pod lib create StringTransform
Cloning `https://github.com/CocoaPods/pod-template.git` into `StringTransform`.
Configuring StringTransform template.

------------------------------

To get you started we need to ask a few questions, this should only take a minute.

2021-06-17 22:47:39.362 defaults[46900:12246301] 
The domain/default pair of (org.cocoapods.pod-template, HasRunBefore) does not exist
If this is your first time we recommend running through with the guide: 
 - https://guides.cocoapods.org/making/using-pod-lib-create.html
 ( hold cmd and double click links to open in a browser. )

 Press return to continue.
```

ここからはウィザードに沿います。

```shell-session:どのようなプラットフォームを使いたいですか？
What platform do you want to use?? [ iOS / macOS ]
 > iOS 
```

私は `iOS` を選択しました。

```shell-session:どのような言語を使いたいですか？
What language do you want to use?? [ Swift / ObjC ]
 > Swift
```

私は `Swift` を選択しました。

```shell-session:ライブラリにデモアプリを含めたいですか？
Would you like to include a demo application with your library? [ Yes / No ]
 > Yes
```

私は `Yes` を選択しました。

```shell-session:どのようなテストフレームワークを使いますか？
Which testing frameworks will you use? [ Quick / None ]
 > None
```

私はXCTestを使っているので `None` を選択しました。

```shell-session:ビューベースドテストをしませんか？
Would you like to do view based testing? [ Yes / No ]
 > No
```

私は `No` を選択しました。

これでウィザードが完了です。

```shell-session:
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint:   git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint:   git branch -m <name>

Running pod install on your new library.

Analyzing dependencies
Downloading dependencies
Installing StringTransform (0.1.0)
Generating Pods project
Integrating client project

[!] Please close any current Xcode sessions and use `StringTransform.xcworkspace` for this project from now on.
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.

 Ace! you're ready to go!
 We will start you off by opening your project in Xcode
  open 'StringTransform/Example/StringTransform.xcworkspace'

To learn more about the template see `https://github.com/CocoaPods/pod-template.git`.
To learn more about creating a new pod, see `https://guides.cocoapods.org/making/making-a-cocoapod`.
```

CocoaPodsプロジェクトが `{ライブラリ名}` フォルダに生成されました。
そこから `{ライブラリ名}.podspec` を抜き出し、プロジェクトのルートに配置します。
それ以外のファイルは使わないのですべて削除します。

つまり `.podspec` ファイルの仕様をわかっていれば `pod lib create` コマンドを実行せずに直接作成しても問題ありません。

### Podの検証

`pod lib lint` を実行してPodを検証します。

```shell-session
$ bundle exec pod lib lint --allow-warnings

 -> StringTransform (0.1.0)
    - WARN  | url: The URL (https://github.com/uhooi/StringTransform) is not reachable.
    - WARN  | url: There was a problem validating the URL https://twitter.com/the_uhooi>.
    - ERROR | xcodebuild: Returned an unsuccessful exit code. You can use `--verbose` for more information.
    - NOTE  | xcodebuild:  note: Using new build system
    - NOTE  | xcodebuild:  note: Building targets in parallel
    - NOTE  | xcodebuild:  note: Using codesigning identity override: -
    - NOTE  | xcodebuild:  note: Planning build
    - NOTE  | xcodebuild:  note: Constructing build description
    - NOTE  | [iOS] xcodebuild:  warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.4.99. (in target 'App' from project 'App')
    - NOTE  | xcodebuild:  warning: Skipping code signing because the target does not have an Info.plist file and one is not being generated automatically. (in target 'App' from project 'App')
    - NOTE  | [iOS] xcodebuild:  warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.4.99. (in target 'Pods-App' from project 'Pods')
    - ERROR | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:9:9: error: 'applyingTransform(_:reverse:)' is only available in iOS 9.0 or newer
    - NOTE  | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:9:9: note: add 'if #available' version check
    - NOTE  | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:8:17: note: add @available attribute to enclosing instance method
    - NOTE  | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:3:1: note: add @available attribute to enclosing extension
    - ERROR | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:9:28: error: 'hiraganaToKatakana' is only available in iOS 9.0 or newer
    - NOTE  | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:9:28: note: add 'if #available' version check
    - ERROR | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:16:9: error: 'applyingTransform(_:reverse:)' is only available in iOS 9.0 or newer
    - NOTE  | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:16:9: note: add 'if #available' version check
    - NOTE  | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:15:17: note: add @available attribute to enclosing instance method
    - ERROR | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:16:28: error: 'hiraganaToKatakana' is only available in iOS 9.0 or newer
    - NOTE  | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:16:28: note: add 'if #available' version check
    - NOTE  | [iOS] xcodebuild:  warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.4.99. (in target 'StringTransform' from project 'Pods')
    - NOTE  | xcodebuild:  note: Using codesigning identity override: 
    - NOTE  | [OSX] xcodebuild:  error: UIKit is not available when building for macOS. (in target 'StringTransform' from project 'Pods')

[!] StringTransform did not pass validation, due to 5 errors.
You can use the `--no-clean` option to inspect any issue.
```

私はたくさんエラーと警告が出たのでひとつずつ修正します。

```shell-session
    - WARN  | url: The URL (https://github.com/uhooi/StringTransform) is not reachable.
```

私はライブラリ名とリポジトリ名が異なるので、自動生成されたURLを修正する必要がありました。

```diff_ruby:StringTransform.podspec
- s.homepage         = 'https://github.com/uhooi/StringTransform'
+ s.homepage         = 'https://github.com/uhooi/swift-string-transform'
```

なぜか検証が通るのですが、 `s.source` のURLも修正する必要があります。

```diff_ruby:StringTransform.podspec
- s.source           = { :git => 'https://github.com/uhooi/StringTransform.git', :tag => s.version.to_s }
+ s.source           = { :git => 'https://github.com/uhooi/swift-string-transform.git', :tag => s.version.to_s }
```

```shell-session
    - WARN  | url: There was a problem validating the URL https://twitter.com/the_uhooi>.
```

URLの末尾に `>` が残っていたので消す必要がありました。

```diff_ruby:StringTransform.podspec
- s.social_media_url = 'https://twitter.com/the_uhooi>'
+ s.social_media_url = 'https://twitter.com/the_uhooi'
```

```shell-session
    - ERROR | xcodebuild: Returned an unsuccessful exit code. You can use `--verbose` for more information.
...
    - ERROR | xcodebuild:  /Users/uhooi/Documents/Repos/GitHub/uhooi/swift-string-transform/Sources/StringTransform/String+Transform.swift:9:9: error: 'applyingTransform(_:reverse:)' is only available in iOS 9.0 or newer
```

`applyingTransform(_:reverse:)` メソッドはiOS 9.0から対応しているのに、 `8.0` を指定していたので修正します。

https://developer.apple.com/documentation/foundation/nsstring/1407787-applyingtransform

```diff_ruby:StringTransform.podspec
- s.ios.deployment_target     = '8.0'
+ s.ios.deployment_target     = '9.0'
```

```shell-session
    - NOTE  | [OSX] xcodebuild:  error: UIKit is not available when building for macOS. (in target 'StringTransform' from project 'Pods')
```

`UIKit` はmacOSだと使えないというエラーですが、そもそも `UIKit` に依存していないのになぜか指定していたので修正します。

```diff_ruby:StringTransform.podspec
- s.framework        = 'UIKit'
+ s.framework        = 'Foundation'
```

一通り修正したので再度 `pod lib lint` を実行します。

```shell-session
$ bundle exec pod lib lint --allow-warnings

 -> StringTransform (0.1.0)
    - NOTE  | xcodebuild:  note: Using new build system
    - NOTE  | xcodebuild:  note: Building targets in parallel
    - NOTE  | xcodebuild:  note: Using codesigning identity override: -
    - NOTE  | xcodebuild:  note: Planning build
    - NOTE  | xcodebuild:  note: Constructing build description
    - NOTE  | xcodebuild:  warning: Skipping code signing because the target does not have an Info.plist file and one is not being generated automatically. (in target 'App' from project 'App')
    - NOTE  | xcodebuild:  note: Using codesigning identity override: 

StringTransform passed validation.
```

`{ライブラリ名} passed validation.` と出力されたので検証に成功しました。

ちなみに最終的に `.podspec` ファイルは以下のようになりました。

```ruby:StringTransform.podspec
Pod::Spec.new do |spec|
  spec.name             = 'StringTransform'
  spec.version          = '0.1.0'
  spec.license          = { :type => 'MIT', :file => 'LICENSE' }
  spec.summary          = 'Transform strings easily in Swift.'
  spec.homepage         = 'https://github.com/uhooi/swift-string-transform'
  spec.author           = { 'uhooi' => 'the_uhooi@yahoo.co.jp' }
  spec.social_media_url = 'https://twitter.com/the_uhooi'
  spec.source           = { :git => 'https://github.com/uhooi/swift-string-transform.git', :tag => spec.version.to_s }
  spec.source_files     = 'Sources/**/*.swift'
  spec.swift_versions   = ['5.1', '5.2', '5.3', '5.4']
  spec.framework        = 'Foundation'

  spec.ios.deployment_target     = '9.0'
  spec.osx.deployment_target     = '10.11'
  spec.tvos.deployment_target    = '9.0'
  spec.watchos.deployment_target = '2.0'
end
```

`spec.source_files` はSwift Packageでフォルダパスをデフォルトから変えていない場合、 `'Sources/**/*.swift'` を指定します。

`spec.swift_versions` はSwift Package Indexのバッジと合わせるのがいいです。
<img width="236" alt="スクリーンショット 2021-06-23 23.29.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/bedc6e76-e4bf-dc40-6209-0bb2c359346d.png">

`spec.○○.deployment_target` も `Package.swift` と合わせるのがいいです。

```swift:Package.swift
// ...
let package = Package(
    // ...
    platforms: [
        .iOS(.v9),
        .macOS(.v10_11),
        .tvOS(.v9),
        .watchOS(.v2),
    ],
    // ...
)
```

### トランクをセットアップする

トランクは認証およびCocoaPods APIサービスです。
ライブラリをCocoaPodsに公開するには、トランクに登録して現在の端末で有効なセッションを持つ必要があります。

`pod trunk register` を実行してトランクに登録します。

```shell-session
$ bundle exec pod trunk register {メールアドレス} '{ユーザー名}' --description='{端末の概要}'
```

入力したメールアドレスにメールが届くので、クリックして認証します。

その後、 `pod trunk me` を実行してセッションを持っていることを確認します。

```shell-session
$ bundle exec pod trunk me
```

情報が出力されたらセッションを持っています。

### CocoaPodsへデプロイする

ここまで来たらあとは `pod trunk push` でCocoaPodsへデプロイするのみです。

```shell-session
$ bundle exec pod trunk push --allow-warnings
```

ただ私はGitHub Actionsで自動化したかったため、手動でデプロイはしませんでした。

### CDを構築する（任意）

GitHub Actionsを使い、自動でCocoaPodsへデプロイするCDを構築します。

```yaml:release.yml
name: Release

on:
  release:
    types: [published]

jobs:
  deploy-cocoapods:
    runs-on: macOS-latest
    steps:
      - uses: actions/checkout@v2
      - name: Install bundled Gems
        run: bundle install --without=documentation --jobs 4 --retry 3
      - name: Deploy to CocoaPods
        env:
          COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
        run: bundle exec pod trunk push --allow-warnings
```

`pod lib lint` は `pod trunk push` に含まれているので実行不要です。

これでGitHubからリリースするたびに自動でCocoaPodsへデプロイされます。

`COCOAPODS_TRUNK_TOKEN` は `~/.netrc` の `password` がそれに当たるので、コピーしてGitHubのシークレットに追加します。

`pod trunk push` で以下のエラーが発生する場合、名前が他のライブラリと被っています。

```shell-session
[!] {"name"=>["is already taken"]}
```

CocoaPodsの公式サイトで検索し、ヒットしない名前に変える必要があります。

https://cocoapods.org/

```diff_ruby:UHIHTTPClient.podspec
Pod::Spec.new do |spec|
- spec.name             = 'HTTPClient'
+ spec.name             = 'UHIHTTPClient'
end
```

### おまけ：スペックへの追加

`pod trunk push` に成功すると、 `CocoaPods/Specs` リポジトリに自動でコミットが作成されます。

https://github.com/CocoaPods/Specs/commit/ebeeb906c6be46d40d68e778555e33e1b0bb5773

### おまけ：CocoaPodsのバッジ

以下のバッジをREADMEに付けることで、CocoaPodsにデプロイしている最新バージョンが自動で表示されます。

[![CocoaPods Version](https://img.shields.io/cocoapods/v/StringTransform.svg)](https://cocoapods.org/pods/StringTransform)

ライブラリ名は適宜変えてください。

## おわりに

自作パッケージをCocoaPodsに追加することができました！
自分のパッケージがCocoaPodsで使えるようになるのはとても嬉しいです :relaxed:

## 参考リンク

- [CocoaPods Guides - Making a CocoaPod](https://guides.cocoapods.org/making/making-a-cocoapod.html)
- [CocoaPods Guides - Using Pod Lib Create](https://guides.cocoapods.org/making/using-pod-lib-create)
- [CocoaPods Guides - Getting setup with Trunk](https://guides.cocoapods.org/making/getting-setup-with-trunk)
- [SwiftPrettyPrint/SwiftPrettyPrint.podspec at 1.2.0 · YusukeHosonuma/SwiftPrettyPrint](https://github.com/YusukeHosonuma/SwiftPrettyPrint/blob/1.2.0/SwiftPrettyPrint.podspec)
- [SwiftPrettyPrint/Makefile at 1.2.0 · YusukeHosonuma/SwiftPrettyPrint](https://github.com/YusukeHosonuma/SwiftPrettyPrint/blob/1.2.0/Makefile)
- [SwiftPrettyPrint/release.yml at 1.2.0 · YusukeHosonuma/SwiftPrettyPrint](https://github.com/YusukeHosonuma/SwiftPrettyPrint/blob/1.2.0/.github/workflows/release.yml)
- [MondrianLayout/MondrianLayout.podspec at 0.0.1 · muukii/MondrianLayout](https://github.com/muukii/MondrianLayout/blob/0.0.1/MondrianLayout.podspec)
- [MondrianLayout/CocoaPodsRelease.yml at 0.0.1 · muukii/MondrianLayout](https://github.com/muukii/MondrianLayout/blob/0.0.1/.github/workflows/CocoaPodsRelease.yml)
- [ワークフローをトリガーするイベント - GitHub Docs](https://docs.github.com/ja/actions/reference/events-that-trigger-workflows)
- [GitHub Actions で 自作Cocoapodsライブラリを自動デプロイする - Qiita](https://qiita.com/ry-itto/items/0fab3fdc4321bfbd3877)
- [Release CocoaPods by uhooi · Pull Request #7 · uhooi/swift-string-transform](https://github.com/uhooi/swift-string-transform/pull/7)
- https://twitter.com/the_uhooi/status/1405522330579738625?s=20