PrivateなCocoaPodsライブラリの作り方

こんにちは、皆さんエッジで機械学習してますか?僕はしています。
CTO室AI推進部の幸野です。
CTO室AI推進部については、以前noteを書いたのでこちらを御覧ください。
AI推進部って何だ?
初めての技術ブログ投稿となる今回は、iPhone, iPadで機械学習(ML)を実行すべく非公開なCocoaPodsライブラリを作成した話です。

背景

MLモデルをサービスに導入する際、スマートフォンで実行できると、サーバとの通信が発生しないためパフォーマンスに大きく利があります。また、ユーザのセキュリティも担保できます。
一方、アプリにML機能を追加する場合、MLに馴染みのないアプリエンジニアに実装をお願いすることは学習コストの観点から厳しいことも多いですし、MLエンジニアがアプリのソースコードに手を入れると密結合となり、保守性が下がってしまいます。
そこで、Swift向けの依存管理ツールであるCocoaPodsでML機能を提供し、アプリと疎結合な関係にすることで開発しやすい環境を実現しました。

モチベーション

「iOSアプリ開発もSwiftも何も分からん」状態で、社内向けの非公開CocoaPodsを作成するにあたり、情報が散らばっており少し苦労したため、非公開CocoaPodsの作り方をここに纏めます。

用意するもの

  • Xcode(13.0)
  • GitHubレポジトリ
    • プロジェクト用とCocoaPods用1つずつ
  • CocoaPods
  • Pod化したいプロジェクト

作り方

公開/非公開であれ、途中までは作り方は同じです。まずは、公開/非公開で分岐するところまで進め、その後非公開対応に進みます。

XcodeでFrameworkプロジェクトを作成する

実装

実現したい処理を実装します。
sayHello()'Hello from Money Forward Engineers' Blog!'を返すだけのサンプルプロジェクトを用意したので、こちらを使います。

プロジェクト用のリモートレポジトリへpush

今回はブログ用に公開レポジトリを使用していますが、社内向けであれば非公開レポジトリを使用します。

ライセンスファイル追加

GitHub公式手順に従い、GitHub上でライセンスファイルを追加します。
GitHub上で作成した後は、ローカルでgit pullコマンドを実行してローカルへも反映させます。
この手続きは、CocoaPodsへ登録する際にライセンスファイルが必要なため行っています。
非公開では無視することもできますが、CocoaPodsでは公開するライブラリが多いため、定められた形式に従います。

podspecファイルの作成

プロジェクトのルートディレクトリでpod spec create <ライブラリ名>を実行し、<ライブラリ名>.podspecを作成します。
今回の例では、pod spec create Greetingを実行した結果、Greeting.podspecが作成されます。
テンプレートを修正し、pod lib lint --allow-warningsで文法確認を行い、エラーがあれば修正します。
今回はコメントアウトしていますが、依存関係があればspec.dependencyに記載します。

Pod::Spec.new do |spec|
  spec.name         = "Greeting"
  spec.version      = "0.0.1"
  spec.summary      = "This is a CocoaPods sample project for MoneyForward tech blog."

  spec.description  = <<-DESC
  Greeting class returns "Hello" when sayHello func is called.
  Sample usage:
  let greeting = Greeting()
  print(greeting.sayHello()) // "Hello"
                   DESC

  spec.homepage     = "https://github.com/KonoTatsuya/greeting-swift/tree/main"
  spec.license      = "Apache License, Version 2.0"
  spec.author             = { "KonoTatsuya" => "kono.tatsuya@moneyforward.co.jp" }

  spec.platform     = :ios, "14.0"
  spec.ios.deployment_target = "14.0"

  spec.source       = { :git => "git@github.com:KonoTatsuya/greeting-swift.git", :tag => "#{spec.version}" }

  spec.source_files  = "Greeting/Greeting/*.{h,swift}"
  # spec.dependency "DependingLibrary"
end

podspecの作成が完了したらリモートレポジトリへpushします。

タグ付け

前項目で使用したタグ(0.0.1)を最新コミットに適応します。

git tag 0.0.1
git push origin 0.0.1

ひと休み

プロジェクトにおける作業はここで終了です。お疲れ様でした。これからは、非公開CocoaPodsライブラリを作成していきます。

CocoaPods管理用非公開リモートレポジトリ作成

CocoaPodsでライブラリを使用するためには、プロジェクトのレポジトリに加えて、CocoaPods管理用のリモートレポジトリ(本記事ではsampleSpecRepoと呼びます)も必要です。
公開ライブラリであれば、CocoaPods/Specsで管理していますが、今回は非公開で管理したいので、非公開のレポジトリを作成します。インターネットには非公開とするが、社内では公開する場合、所属するOrganizationに対しては公開設定としてください。
レポジトリ作成後のコマンドは下記を実行します。
デフォルトブランチをmainとするのが現在の主流ですが、CocoaPodsではmasterを使うことに注意してください。

echo "# sampleSpecRepo" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin 
git@github.com:<GitHubUserName>/<RepoName>.git
git push -u origin master

本記事のためにサンプルのレポジトリを作成したので、ご参考ください。https://github.com/KonoTatsuya/sampleSpecRepo

podspecファイルをsampleSpecRepoに追加する

ローカルのCocoaPodsにsampleSpecRepoも管理対象であることを認識させた後、先程作ったpodspecファイルをsampleSpecRepoへ追加します。

ローカルのCocoaPodsにsampleSpecRepoも管理対象であることを認識させる

pod repo add myPrivatePods(任意の名前) git@github.com:/.git
このコマンドを実行することで、ローカルの~/.cocoapods/reposmyPrivatePodsというディレクトリが作成されます。
本記事の例では、pod repo add myPrivatePods git@github.com:KonoTatsuya/sampleSpecRepo.gitとなります。

なお、公開されているPodの情報は~/.cocoapods/repos/trunkで管理されています。

podspecファイルをsampleSpecRepoに追加する

Greeting.podspecが含まれるディレクトリで、pod repo push myPrivatePods Greeting.podspec --allow-warningsを実行します。
このコマンドはpodspecファイルを解析し、指定したローカルのCocoapodsディレクトリ(今回はmyPrivatePods)にライブラリを追加すると共に、sampleSpecRepoへpodspecファイルをプッシュします。
成功すると、下記ディレクトリ構造でsampleSpecRepoにファイルが追加されます。

sampleSpecRepo
├── Greeting(pod name)
    └── 0.0.1(version)
        └── Greeting.podspec

これで非公開CocoaPodの完成です。
後は実際にPodとして使えるかどうか、確認してみます。

Podとして使えるか確認する

Podfileの先頭にsource 'git@github.com:/.git'の形でsampleSpecRepoのURLを追加し、他は通常のPodfileと同じように記載します。

source 'git@github.com:KonoTatsuya/sampleSpecRepo.git'

platform :ios, '14.0'
use_frameworks!

target 'techBlogSampleApp' do
  pod 'Greeting'
end

pod installを実行するとインストール成功したことが確認できます。

Analyzing dependencies
Downloading dependencies
Installing Greeting (0.0.1)
Generating Pods project
Integrating client project
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.

ContentView.swift

import SwiftUI
import Greeting

struct ContentView: View {
    var body: some View {
        let greeting = Greeting()
        let hello = greeting.sayHello()
        Text(hello)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

無事”Hello from Money Forward Engineers’ Blog!”の出力を確認できました。

まとめ

AI推進部では、機械学習モデルの開発に留まらない、多岐に渡る技術に触れることができます。
機械学習をサービスへ導入したい想いをお持ちの方、ぜひ下記求人からご応募ください!

【機械学習エンジニア】CTO室(AI推進部)_東京(田町)
【機械学習エンジニア】CTO室(AI推進部)_京都


マネーフォワードでは、エンジニアを募集しています。
ご応募お待ちしています。

【サイトのご案内】
マネーフォワード採用サイト
Wantedly
京都開発拠点

【プロダクトのご紹介】
お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android

ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』

おつり貯金アプリ 『しらたま』

お金の悩みを無料で相談 『マネーフォワード お金の相談』

だれでも貯まって増える お金の体質改善サービス 『マネーフォワード おかねせんせい』

金融商品の比較・申し込みサイト 『Money Forward Mall』

くらしの経済メディア 『MONEY PLUS』

Pocket