📦

振り返りながら学ぶPackage Manager(原稿)#iosdc #e

2021/09/19に公開

これはiOSDC Japan 2021での下記のセッションの原稿です。

https://fortee.jp/iosdc-japan-2021/proposal/1d9f4c94-21c2-4e38-a440-fcd01b3a5826

iOSアプリ開発に用いるCocoaPods, Carthage, SwiftPMといったPackage Managerについて、基本的な概念と仕組み、歴史を学んでいきます。

想定しているターゲットは以下のような「Package Manager詳しくない勢」の方です。

  • Package Managerの使い方はわかるけど仕組みはよくわからな方
  • あまり詳しくないけどPackage Managerの移行等を検討する必要がある方
  • アプリ開発を学び始めてPackage Managerを使い始めた方

これまで歴代バージョンのPackage Mangerと格闘してきた「Package Manager詳しい勢」の方は、「ああ、そういえばそういうのあったよね」と思い出に浸るなり、新人教育用の資料にシェアしていただくなどしていただけると幸いです。

動画はこちら
https://www.youtube.com/watch?v=s1EIF-mUUD8

スライドはこちら
https://speakerdeck.com/jollyjoester/zhen-rifan-rinakaraxue-hupackage-manager

↓↓↓以下原稿↓↓↓

振り返りながら学ぶPackage Manager

振り返りながら学ぶPackage Manager

それでは「振り返りながら学ぶPackage Manager」 というタイトルで発表します!

自己紹介

自己紹介

どーも!ジョリージョースターです。
iPhoneに魅せられてエンジニアに転向してから早8年
未だに楽しくiOSアプリ開発を追っています。

ほぼ毎月Swift愛好会という、SwiftやiOSをわいわい楽しむ会をやっています。
毎回「かーんぱーい🍻」から始まるゆるい会ですので一緒に楽しみたい方ぜひご参加ください!

iOSDCでの発表は4年ぶり2回目!
とても嬉しいです。よろしくお願いします!

このセッションについて

このセッションについて

このセッションではPackage Managerまだ詳しくない勢の方が

「基本的な概念や仕組みの知識を得て、より適切にPackage Managerとお付き合いできるようになること」

を目標としています。

「なんかCarthageでエラーが出てるけどわからん→爆死」
「SwiftPMが流行ってる!よくわからないけど早く移行しなきゃ→爆死」
みたいな悲劇に適切に立ち向かえるようにしていきましょう!

アジェンダ

アジェンダ

アジェンダです。

そもそもPackage Managerとは何かおさらいしてから、iOSにおける代表的なPackage Managerについてその概要と仕組みを見ながら確認していきます。

最後にPackage Managerを取り巻くiOSやSwiftの進化を振り返り、まとめていきます。

Package Managerとは

Package Managerとは

それではPackage Managerとは何か?からやっていきましょう〜

Package Manager

Package Manager

Package Managerとは「各種のソフトウェアの導入と削除、そしてソフトウェア同士やライブラリとの依存関係を管理するシステムである」 by wikipedia先生です。

Packageとは導入するソフトウェアの塊といったイメージでしょうか。

いろいろな範囲を対象としたPackage Managerがあります。

例えば

  • APTのようなLinuxというOSが対象のもの
  • RubyGemsのようにRubyという言語環境が対象のもの
  • そして今回対象とするiOS界隈で用いられているような複数言語の特定のターゲットを対象にしたもの

があります。

Package Managerの機能

Package Managerの機能

おおよそのPackage Managerはここに示す機能を持っています。

まずどんなパッケージがあるか探す機能。

次に必要なパッケージのインストール・削除の機能。パッケージ名やバージョンを指定してパッケージのインストールや削除を行います。

そして依存関係の解決。これは必要なパッケージを導入するにあたって必要な別のパッケージやライブラリを自動的にインストールしたりすることです。

設定の管理。設定ファイルを記述することで設定の管理をすることができます。

いったんここではバクっとした理解にとどめておき、次に行きましょう。

iOSにおけるPackage Manager

iOSにおけるPackage Manager

今回詳しく知っていきたいiOSにおけるPackage Managerについて見てきます。

iOSにおけるPackage Manager

iOSにおけるPackage Manager

iOSにおける代表的なPackage Managerとして

  • CocoaPods
  • Carthage
  • Swift Package Manager

がありますね。

それぞれ概要をざくっとおさらいしてみましょう。

CocoaPodsの概要

CocoaPodsの概要

まずみんな大好きCocoaPodsです。
2011年というかなり初期の頃に登場し、様々な試練を乗り越えてまだまだ現役です。

CocoaPodsという名が示す通り、iOSやmacOSなどといったCocoaアプリケーションをサポートします。

オープンソースとして開発されており、Rubyで開発されています。
Ruby製というのはSwiftがなかった開発当時を考えれば妥当な選択でしょう。

基本的な特徴として、「ライブラリをバイナリで配布するのではなく、ソースコードで配布して、プロジェクトでビルドする」という発想があります。

ライブラリはアプリのビルド時に一緒にビルドします。ここはポイントになってくるので覚えておいてください。

ライブラリをXcode projectに組み込むという複雑なことも自動的にやってくれるのでコマンド一発で便利に使えます。一方でその特性がアプリの肥大化時代に課題になっていきます。それはまた後ほどお話しましょう。

Centralizedというのは中央で管理された、という意味です。CocoaPodsではSpecsというGitHubリポジトリでライブラリのメタデータを中央管理する仕組みを取っています。ライブラリを発見されやすくしてできるだけ使ってもらえるようにしたいという意図があるようです。

この辺はCarthageとも比較しますが、GitHubがまだそれほど浸透していなかった時代の名残を感じます。

Carthageの概要

Carthageの概要

次にCarthageを見ていきましょう。

登場は2014年です。

PlatformはCocoaPodsと同様iOSやmacOSなどのCocoaアプリケーションをサポートしています。

オープンソースとして開発されており、Swift製です。
2014年といえばSwiftが登場してまもなくのことなのですごいチャレンジですね!

基本的な特徴としてCocoaPodsへのアンチテーゼがあります。

まずバイナリベースの利用ということでCarthageではアプリに組み込む前にビルドしたライブラリを利用する発想です。

取得してきたソースはアプリに組み込む前にビルドして使ったり、ビルド済みのバイナリをダウンロードして使うという発想です。

また、CocoaPodsのようにアプリのXcode projectに自動で統合するということをせず、基本触らない方針です。

なのでライブラリをXcode projectに紐付ける際はManualで行います。

依存解決とビルドにフォーカスしたシンプルさを志向しています。

Decentralizedということも謳っています。中央管理の仕組みはなく、基本的に各GitHubのリポジトリを使っていこうという発想です。ライブラリの発見されやすさについてはGitHubが浸透してきたので、そこをPackage Managerで担う必要ないやろ、みたいな思想を感じますね。

Swift Package Manager(Swift PM or SPM)の概要

Swift Package Manager(Swift PM or SPM)の概要

そして来ましたSwift Package Managerです。
みなさん使ってますか?

Swift Package Managerは2017年に登場しました。

サポートするPlatformとしてCocoaアプリケーションに加えてLinuxもサポートしており、Swift言語を使えるあらゆる環境のためのPackage Managerという性格でしょうか。

当初は使い方が限られており、macOS向けのコマンドラインツールやServerSide-Swiftで主に使われていた印象があります。

ですが、Xcode11からiOSアプリ開発でも使いやすくなりました。

基本的な特徴としては、Carthageのように事前にビルドしたframeworkを組み込むようになっているようです。しかし、Xcodeと統合されているのでManualでframeworkを組み込むような手順は不要になっています。

そして標準というのがでかい。

シームレスに使えるといった使い勝手についてもそうですが、これまでSwiftやXcodeの進化ごとにPackage Managerの対応待ちという鬼門があった部分が解消されることへの期待はとても大きいです。

CocoaPodsやCarthageはCocoaアプリケーションのためのパッケージマネージャでしたが、Swift Package ManagerはSwift言語を使えるあらゆる環境のためのパッケージマネージャという性格のためコマンドラインツールやサーバーサイドなどでもSwiftが使いやすくなることに期待できますね。

各Package Managerの仕組み

各Package Managerの仕組み

さてここから本題です。

それぞれのPackage Managerの仕組みについてもう少し詳しく見ていきましょう。

なお、注意事項としてそれぞれ「普通」に使ったときのことをお話します。

各Package Managerはそれぞれ進化しているのでpluginの導入やoptionの設定で多様な使い方ができる、ということも覚えておいていただけると幸いです。

CocoaPodsの使い方 ~ 提供者側 ~

CocoaPodsの使い方 ~ 提供者側 ~

まずCocoaPodsから

使用者側の視点で関わる方が多いと思うのでライブラリの提供者側から見ていきましょう。

だいぶ端折っていますが大きくは

①開発して、
②GitHub等のリポジトリにソース等を格納して
③ライブラリのメタデータをSpecsに登録する

という流れです。

開発の部分は指定のディレクトリ構造にしたりテストしたりといろいろあるのですがそこはドキュメントを参照ください。

リポジトリとメタデータがそれぞれある状態ですね。

CocoaPodsの仕組み ~ 使用者側 ~

CocoaPodsの仕組み ~ 使用者側 ~

そして使用者側です。

①pod initでPodfileを作ります。
②そこにライブラリ名とバージョンなどを必要事項を記載します。
pod install をするとCocoaPodsが自動的にいろいろやってくれます。
④xcworkspaceができるのでそれを開けばライブラリが使えるようになり、アプリをビルド、実行することができます。

CocoaPodsの仕組み ~ 使用者側 ~

CocoaPodsがいろいろやってくれている部分を見てみましょう。

pod install されたらPodfileに従ってまずSpecsを見に行きます。
ここにライブラリのメタデータがあるのでライブラリの置き場所を把握し、置き場所からソース等をダウンロードします。

本当はここで行われる依存の解決などが面白い部分だと思いますが僕には難しすぎるので割愛させてくださいw

ダウンロードしたソース等はPodsというディレクトリが作成されて格納されます。

どのライブラリのどのバージョンをダウンロードしたという記録をPodfile.lockにします。
このファイルが環境を再現する際などに重要になってきます。

Podsディレクトリにあるファイル郡はPods.xcodeprojというまとまりで管理され、アプリ本体のprojectとの設定も作成されます。

そしてprojectをまとめられるxcworkspaceで2つのprojectをまとめて完了です。

先程ソースベースの利用と説明しましたが、このようにライブラリをソースコードとして取り込んでいます。なのでアプリがビルドされる際にライブラリもビルドされます。一度ビルドされたものはキャッシュされますが、Cleanビルドなどのときは再度ビルドされます。

この仕組みはアプリの規模が小さいうちはあまり困らないのですがアプリの規模が大きくなると
ビルド時間が長くなるという問題を引き起こします。

ビルド時間が長くなると、開発中にTwitterする時間が増えてしまったり、CIが長引いて余計に課金されてしまったりと困ります。

また自動統合の仕組みは便利なのですが、project設定が複雑になりすぎる問題も発生します。

Carthageの使い方 ~ 提供者側 ~

Carthageの使い方 ~ 提供者側 ~

Carthageの提供者側も見てみましょう!

Carthageの場合は

①開発して
②リポジトリにソース等を格納

です!

シンプル〜

Carthageの使い方 ~ 使用者側 ~

Carthageの使い方 ~ 使用者側 ~

使う側です。

①まずCartfileを書きます。
②コマンドを叩いてインストールします。
③生成されたライブラリのファイルをManualでprojectに紐付けます。
④アプリを実行できます

Carthageの使い方 ~ 使用者側 ~

Carthageがやってくれる部分を見ていきましょう

Cartfileを元に依存を解決してライブラリがある場所からソースコードをダウンロードします。ダウンロードしたソースはCarthageディレクトリの下にcheckoutsディレクトリが作成され、そこに格納されます。

Podfile.lockと同様の役割のCartfile.resolvedというファイルも作成されます。

ダウンロードが終わるとビルドされ、ビルドされたバイナリがBuildディレクトリに格納されます。

Manualで追加するのはこのBuild以下に生成されたファイルでこれをprojectに追加していきます。

最初にライブラリをビルドする際に時間がかかりますが、事前にビルドしておく分、アプリのビルドは早くなります。

シンプルだしビルドも早くなって良さそうですね!

ただ、いくつか注意点もあります。
1つ目はシンプルな分、使う側がcarthageコマンドのオプションやXcodeのprojectやframeworkについての理解がないと適切に運用するのが難しいことです。

例えばコマンドのオプション
-- platform iOS を指定しないと他のPlatform向けのビルドも作成されてしまい時間がかかります。

-- use-xcframeworkはバイナリ配布のための比較的新しい仕組みですが、これを使わない場合、Manualでcopy-frameworkをする等必要な手順が増えます。

ビルド済みバイナリをダウンロードして使うこともできるのですが、アプリとライブラリのSwiftバージョンが異なって使えないなどありました。

とはいえ、SwiftやXcodeなどの進化のおかげでやっとCarthageの描く世界が実現しやすくなってきたような印象があり、過去よりも簡単に使えるようになってきたと感じています。

Swift Package Managerの使い方 ~ 提供者 ~

Swift Package Managerの使い方 ~ 提供者 ~

さぁ来ましたSwift Package Managerです。

こちらも提供者側から見ていきましょう。

Carthageの場合と似ています。

異なるのはPackage.swiftという設定ファイル、マニフェストファイルと呼ばれますがこれをソース等と一緒にリポジトリに格納します。

このファイルがあるかを見ればSwift Package Manager対応されているということですね!

Swift Package Managerの使い方 ~ 使用者側 ~

Swift Package Managerの使い方 ~ 使用者側 ~

使い方です。

そうです。Xcodeから設定できます!

ProjectにSwift Packageという項目があるのでここからリポジトリのURLとバージョンを指定するとダウンロード&ビルドが走ってアプリ中で使えるようになります。

Swift Package Managerの使い方 ~ 使用者側 ~

仕組みはおおよそCarthageに似ている感触です。

ソースコードがダウンロードされ、すぐビルドされます。
これらはDerivedDataの下のSourcePackageやBuildディレクトリに格納されます。

そして嬉しいことにManualな作業なしにprojectにリンクされます。

標準ばんざ〜い!

基本的な共通の仕組み

基本的な共通の仕組み

ということで見てきました。

共有の仕組みとしては

  • ライブラリ提供者側はGitHubなどにライブラリ置く、場合によってメタデータ的なものも置くと。

使用者側は

  • 何かしらの設定ファイルにライブラリの場所とバージョンを指定して
  • ソース等をダウンロードして
  • ビルドして
  • プロジェクトに組み込んで
  • 使う

という流れでした。それらを実現するためのやり方に様々な選択肢があることも見てきました。

実は各Package Managerも進化していてそれぞれ同様のことを実現できたりします。例えばCocoaPodsでCarthageのように事前ビルドしたframeworkを使いたいといったこともできるので、この辺のポイントや選択肢を押さえておくと調査が捗ると思います。

周辺技術の歴史と進化

周辺技術の歴史と進化

仕組みを見てきたので、最後にiOSにおけるPackage Managerに関連深い技術の歴史を振り返りつつ、どのように進化してきたか振り返ってみましょう。

Package ManagerたちはiOS, Xcode, Swiftの進化とともに様々な壁にぶつかって乗り越えてきました。

その過程にはいろいろ学びがあるはずです。

いろいろなキーワードが出てきますが深堀りせずザクザクいきます!

iOSアプリ開発におけるPackage Managerの歴史

iOSアプリ開発におけるPackage Managerの歴史

このような形でばっくり年表を作ってみました。

ではやっていきましょう。

Before CocoaPods

Before CocoaPods

まずBefore Cocoa Podsです。

この辺は実は僕もあまり知りません。

なんとなくstatic libraryである.aファイルを手動で入れていた記憶があります。

知っている方がいたらぜひ思い出話を聞かせてください

CocoaPods浸透

CocoaPods浸透

次にCocoaPodsが浸透した時代です。

ライブラリ導入といえばCocoaPodsだよねということで多くのライブラリがCocoaPods対応していたと記憶しています。

このときはstatic libraryが主流だったはずです

Swiftの登場

Swiftの登場

そしてSwift登場の衝撃です

Swiftの登場とともにPackage Manager界に大激震が走ります。

このときにiOSでdynamic frameworkという仕組みがサポートされました。

またSwiftのモジュールを使うにはこのframeworkを用いる必要がありました。

CocoaPodsではこのときuse_framework!というdynamic frameworkを使う仕組みを急遽用意します。

この辺dynamic frameworkやstatic frameworkというのは今でもキーワードになるので別途学ぶと良いと思います。

またこの頃Carthageも登場と賑やかな時期ですね。

Swiftの浸透

Swiftの浸透

そしてSwift浸透の時代です。

この頃すでに多くのアプリでSwiftが使われるようになってきました。

この頃Package Managerに関連する多くの課題が表出し始めてきました。

例えばアプリの肥大化。ビルド遅い問題が注目されCocoaPodsからCarthageへの乗り換えが検討されました。

肥大化に伴い多くのライブラリを使うようになりましたが、dynamic frameworkを多用するとロードのためアプリの起動速度が遅くなる問題もありました。

そしてCIの普及。ライブラリをキャッシュして使うなどの対策を取るような検討されはじめたのもこのくらいの時期と記憶しています。

そんな中Swift Package Managerも生まれます。が、活躍するのはまだ先の話・・・

Swift周辺のビルド環境の進化

Swift周辺のビルド環境の進化

そんな課題に対して解決策がいろいろ出てきたのがこの時期です。

異なるバージョンのSwiftでビルドしたバイナリを使えるモジュール安定化

SwiftのモジュールのStatic framework対応

異なるアーキテクチャやPlatform向けのframeworkを1つの形式でまとめられるxcframeworkをサポート!

などなどライブラリのビルド環境が劇的に進化します。

そんなタイミングでSwift Package ManagerがXcodeで使えるようになるという激アツ展開ですね。

もちろんCocoaPodsやCarthageもそれぞれの進化に適応してます。(この辺細かいところ追ってなくてすみませんw)

そして現在へ

そして現在へ

そして現在・・・

まだまだハマるポイントもありますが、様々な課題が解決しつつあると感じています。

Swift Package Managerも実用的になってきており、もしかしていろいろな準備が整いつつある?と感じています。

今後のPackage Managerについての私見

今後のPackage Managerについての私見

さてここまで振り返ってきて今後のPackage Managerについて感じたことを書いてみました。

まずCocoaPods, Carthageも現役で使えることを確認しつつ、特に実用的になってきたSwift Package Managerに期待が高まりました。

特にSwiftやXcodeと深く統合されていることを活かしてこれまでのPackage Managerが超えられなかった変化を期待したいです。

例えばの想像ですが

  • xcodeprojという古い仕組みからの脱却
  • CI利用を前提とした機能
    といったことですね。

各Package Managerは併用できるのでまだ使ったことないという方はSwift Package Managerの検証を始めるちょうど良いタイミングではないでしょうか。

まとめ

まとめ

まとめです。

まとめ

このセッションではPackage Managerの基本的な仕組みと大激動のiOS界のPackage Managerを振り返ってみました。

私自身は、半年以上前にCocoaPodsとCarthageを併用しているアプリをSwift Package Managerに移行しようとして火傷したので一度中止しました。まだ移行に必要な機能をSwift Package Managerが備えていなかったのです。

ただ、私は知識がなかったため、無駄に悩むことになりました。
それが本セッションに繋がっています。

その後不足していた機能が実装され、今はリベンジのタイミングを伺っています。

Swift Package Managerがこれから盛り上がってきている今だからこそPackage Manager全体を振り返るいい機会なのではないかと思い、このテーマを提案しました。

アプリの開発現場は規模や開発力、歴史や利用ユーザー数など様々な状況です。

このセッションが誰かにとってPackage Managerとより良くお付き合いするためのヒントとなれば幸いです。

ありがとうございました!

おまけ

おまけ

最後にいろいろ学んでみて今後の流れについての私見を述べます。

  • CocoaPods, Carthage, Swift Package Managerはそれぞれ進化していて有用になった
  • Swift Package Managerは急激に進化していて実用レベルに
  • そろそろ標準のSwift Package Managerだけサポートしたいというライブラリ提供者が出てくるだろう
  • 各Package Managerは併用できるのでSwift Package Managerを試しはじめておくと良いだろう
    とはいえ1, 2年単位で変化が来ると予想。
  • あせらず自身のProjectとの状況を踏まえあせらず準備していこう。

↑↑↑原稿ここまで↑↑↑

Discussion