CocoaPodsでiOSのライブラリーを作ってみる

## はじめに
僕は本業ではWebアプリ開発を中心として、主にバックエンド側やインフラ（少し）をやっています。フロントエンド側はjs(Vue.js)やCSS（少し）にちょっと触ったことがありますが、ネイティブアプリの開発は全く経験がありませんでした。

最近、会社のサービスの共有認証システムという担当しているシステムはFacebookといったソーシャルログインを導入しようとした時に、Facebookの規約によると、ネイティブでFacebookの専用SDKを利用しなければならないので、ネイティブ側の実装が必要になります。また、共有のシステムで現在複数のサービスに提供していて、それぞれのサービスに対するネイティブアプリがあり、別々に実装したら、効率が悪くて、メンテナンスの面も大変だと思うので、共有のライブラリー（SDK）を作ることにしました。

iOSネイティブアプリの開発は全く経験がなかったけど、学びながらチャンレンジしてみました。（もちろん、ネイティブアプリの経験者の方にサポート、フローしていただきました。）

今回はその経験について書きたいと思います。

## 準備

### 使っていた環境
- macOS 10.15.7
- XCode 12.3
- Swift 5
- Cocoapods 1.9.3

### 知識
- Swift概要（基本的な知識）

ライブラリーを作成するのに、言語は一番重要ですね。
なので、まずSwift言語基本的な知識を勉強しましょう！

Swiftを勉強するには、Swiftの公式サイトを使いました。具体的は[Swift Tour](https://docs.swift.org/swift-book/GuidedTour/GuidedTour.html)と[Language Guide](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html)より勉強しまた。

- iOSネイティブアプリの開発

iOSネイティブアプリ用のライブラリーを作るため、iOSネイティブアプリの開発についても身に付ける必要ですね。

ゼロからアップルストアに公開できるiOSアプリの開発まで学ぶ必要わけではないけど、iOSアプリの開発の基本知識を学ぶべきです。以下はApple公式のチュートリアルとudacityの無料コース（両方とも英語）はおすすめです。

- [Start Developing iOS Apps (Swift)](https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/index.html)
- [Udacity - Intro to iOS App Development with Swift](https://www.udacity.com/course/intro-to-ios-app-development-with-swift--ud585)

## pod開発

概要は以下のようです。

1. Xcodeでプロジェクトを作成し、設定する
2. podを実装する
3. gitと連携する
4. アプリに組み込む

### Xcodeでプロジェクトを作成し、設定する

- 新しくプロジェクトを作成する

Xcodeを開いて、新しくプロジェクトを作成する。
CocoaPodsライブラリーを作りたいので、Frameworkを選択する。

![スクリーンショット 2020-12-22 0.55.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/5533e3f0-8afa-4c6b-2d63-7610b9452862.png)


`MySwiftLib`プロダクト名を入力して、ユニットテストのため「Include Tests」チェックボックスをチェックする

![スクリーンショット 2020-12-22 10.57.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/678637e1-533f-7e91-e33d-acffddb265ba.png)


- ライブラリーの使い方を紹介するためデモアプリを追加する

Xcodeのメニューから`File > New > Target`、「App」というテンプレートを選択する

![スクリーンショット 2020-12-22 10.38.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/21eacb68-8187-fe8c-8707-cb4b000f9f6b.png)


`MySwiftLibExamples`というプロダクト名を入力して、デモアプリでユニットテストが要らないため、「Include Tests」チェックボックスを外します。

![スクリーンショット 2020-12-22 10.58.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/4e2bc076-f6fc-5ee0-6032-c203297178b4.png)

プロジェクトの構成は以下のようです。

![スクリーンショット 2020-12-22 11.06.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/5ddaa054-3553-8e85-ee63-c5a461978909.png)

### podを実装する

### podspecファイルを作成する
podspecファイルはバージョン、ソース箇所やメーター情報などは定義されるところです。

簡単なpodspecは以下のようです。

まず、プロジェクトのディレクトリに以下のコマンドでpodのspecを作成します。

```bash
$ pod spec create MySwiftLib
```

```ruby:MySwiftLib.podspec
Pod::Spec.new do |spec|

  # ―――  Spec Metadata  ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  spec.name         = "MySwiftLib"
  spec.version      = "0.0.1"
  spec.summary      = "My First CocoaPods Library written in Swift"
  spec.homepage     = "https://github/username/MySwiftLib"

  # ―――  Spec License  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  spec.license      = { :type => "MIT" }

  # ――― Author Metadata  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  spec.author             = { "trandoantan" => "tan.tran@example.com" }

  # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  spec.platform     = :ios

  # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  spec.source       = { :git => "https://github/username/MySwiftLib.git", :tag => "#{spec.version}" }

  # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  spec.source_files  = ["MySwiftLib/**/*.swift", "MySwiftLib/MySwiftLib.h"]

  # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  # spec.dependency "FacebookCore"
  # spec.dependency "FacebookLogin"
end
```

- `Metadata`（**必須**）: Pod名、バージョン、サマリー、ホームページ
- ライセンス（**必須**）：　非公開のPodなら、ライセンスのタイプの指定だけでいいです。
- 作者情報（**必須**）
- `platform`（**任意**）：　利用可能のプラットフォーム
- `source`（**必須**）： gitのリポジトリを指定し、バージョンニングのためのタグを指定する（新しいバージョンをリリースした際に、gitのタグを使って指定する）
- `source_files`（**必須**）： ライブラリーに含むソースコードファイル。上記の例ではヘッダーファイルや全てSwiftのファイル
- `dependency`: ライブラリー内に外部のパッケージを使いたい場合、これで指定する

### ライブラリーの実装
`MySwiftLib`に新しくSwiftファイルを追加する。

![スクリーンショット 2020-12-22 19.24.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/319ea653-197a-b761-c93c-0b6813b30914.png)

![スクリーンショット 2020-12-22 19.21.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/1b9e9e5e-3eed-b578-0bce-5b59dad608b1.png)

```swift:MySwiftLib.swift

enum Language: String {
    case japanese = "ja"
    case english = "en"
    
    var greetingTemplate: String {
        switch self {
        case .japanese:
            return "こんにちは、%@！"
        case .english:
            return "Hello, %@!"
        }
    }
}

public class MySwiftLib {
    var defaultLang: Language
    
    public init(lang: String) {
        if let language = Language.init(rawValue: lang) {
            defaultLang = language
        } else {
            defaultLang = .english
        }
    }
    
    public func greetingMessage(userName: String) -> String {
        return String(format: defaultLang.greetingTemplate, userName)
    }
}
```

サンプルなので、とても簡単ですね。

### ユニットテストを書く
次、上記の`MySwiftLib`に対するユニットテストを書きましょう。

- `MySwiftLibTests`にある`MySwiftLibTest.swift`ファイルを以下のように編集します。

```swift:MySwiftLibTest.swift
import XCTest
@testable import MySwiftLib

class MySwiftLibTests: XCTestCase {
    func testGreetingMessageEnglish() {
        let mySwiftLib = MySwiftLib(lang: "en")
        XCTAssertEqual(mySwiftLib.greetingMessage(userName: "John"), "Hello, John!")
    }
    
    func testGreetingMessageJapanese() {
        let mySwiftLib = MySwiftLib(lang: "ja")
        XCTAssertEqual(mySwiftLib.greetingMessage(userName: "Alice"), "こんにちは、Alice！")
    }
    
    func testGreetingMessageDefaultEnglish() {
        let mySwiftLib = MySwiftLib(lang: "hogehoge")
        XCTAssertEqual(mySwiftLib.greetingMessage(userName: "John"), "Hello, John!")
    }
}
```

- ユニットテストを実行する

![スクリーンショット 2020-12-22 19.34.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/07a224bb-dfc7-d60f-66a6-da491b09e375.png)

### Examples実装
次、デモのため、`MySwiftLibExamples`を実装します。

- `Main.storyboard`にLabelを追加して、`ViewController`は以下のように編集します。

![スクリーンショット 2020-12-22 19.40.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/9b1e67bc-b6a1-01fe-aa76-a209596c3e8e.png)


```swift:ViewController.swift
import UIKit
import MySwiftLib

class ViewController: UIViewController {
    //MARK: Properties
    @IBOutlet weak var greeting: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let mySwiftLib = MySwiftLib(lang: "ja")
        self.greeting.text = mySwiftLib.greetingMessage(userName: "Tan")
    }
}
```

- `MySwiftLibExamples`をビルドしてみたら、以下のようです。:tada::tada::tada:

![スクリーンショット 2020-12-22 19.46.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117115/c93d3259-b90c-16b2-ebc7-b79a3256407a.png)

## gitと連携する

- githubとかで`MySwiftLib`リポジトリを作成する
- プロジェクトのディレクトリーにgitを初期化して、作成したリポジトリにコードをプッシュする

```bash
$ cd /your/project/path
$ git remote add origin git@github.com:username/repo-name.git
$ git add .
$ git commit -m "initial commit"
$ git push origin master
```

- （必要であれば）作成したリポジトリによって`podspec`を更新する

```ruby:MySwiftLib.podspec
  spec.homepage     = "https://github.com/username/repo-name"
  spec.source       = { :git => "https://github.com/username/repo-name.git", :tag => "#{spec.version}" }
```

- タグを付けて、最新のコードをプッシュする

```bash
$ git tag 0.0.1
$ git push origin --tags master
```

こちらは個人使用のpodなので、podを公開しない。
podを公開したい方は[ここ](https://medium.com/flawless-app-stories/create-your-own-cocoapods-library-da589d5cd270#8f0d)に参考してみてください。

## アプリに導入

- podをアプリに導入するには、`Podfile`に以下のコードを追加する

```ruby
    # for https: pod 'MySwiftLib', :git => 'https://github.com/username/repo-name.git'
    pod 'MySwiftLib', :git => 'git@github.com:username/repo-name.git'
```

こちらのpodは公開しないので、gitでpodを指定する

**開発中に、`git`を使ったら、毎回install/updateコマンドで更新を反映させるのは不便なので、podを導入先のアプリの同じディレクトリーに置けば、以下のような設定でパスでpodを指定できて、更に更新をすぐ反映されます。**

```ruby
   # for https: pod 'MySwiftLib', :git => 'https://github.com/username/repo-name.git'
   # pod 'MySwiftLib', :git => 'git@github.com:username/repo-name.git'
   pod 'MySwiftLib', :path => '../MySwiftLib'
```

- 以下のコマンドを実行して、アプリにpodをインストールする

```bash
$ pod install
# 更新の場合
$ pod update MySwiftLib
```

インストールした後に、`MySwiftLibExamples`のように、モジュールを`import`したら、使えます。

## 終わり

今回はCocoapodを使って、簡単なpodのライブラリーを作成する方法を紹介しましたが、iOSアプリの開発にまだ初心者ですので、こちらの記事に何か間違いがありましたら、ご指摘いただければ幸いです。

ご拝読いただきありがとうございます！

## 参考
- https://medium.com/flawless-app-stories/create-your-own-cocoapods-library-da589d5cd270
- https://guides.cocoapods.org