Objective-CからSwiftへ、4つの移行ポイント~メルカリの実践例から最適な手法を学ぶ

多くの企業でObjective-CからSwiftへの移行が行われていますが、どのような戦略、手順が必要になるのでしょうか。実践に基づくノウハウを、メルカリの小林晋士さんが解説します。

Objective-CからSwiftへ、4つの移行ポイント~メルカリの実践例から最適な手法を学ぶ

2014年にSwiftが登場して以来、その利便性の高さから多くのiOSエンジニアがこの言語を用いるようになりました。それに伴い、Objective-Cで書かれたアプリケーションをSwiftに移行する企業も増えています。フリマアプリ「メルカリ」の開発・運営で知られる株式会社メルカリも、そのひとつです。本稿では、TOPLOG株式会社と株式会社メルカリの2社でObjective-CからSwiftへの移行を経験した小林晋士さんに、移行にあたり策定すべき戦略とポイントについて解説していただきました。

なぜSwiftで作るのか

iOSがリリースされた当初、ほとんどのアプリは当然Objective-Cで作られていました。オフィシャルにサポートしている言語としてはObjective-C++もありましたが、これはあくまでObjective-CとC++のブリッジング以上のものではなく、あまり積極的に用いられるものではありませんでした。

筆者個人としては長らくC++でプログラミングしており、同言語が持つ機能を好んでいたため、UI以外をすべてC++で実装したアプリケーションを作ったこともあります。その時作成していたアプリで扱うデータを管理するには、C++の強力な機能である多重継承とtemplateが必要だったからです。

2014年のWWDCでSwiftが発表された際はSwiftという言語自体を学ぶことが話題の中心でした。当時、議論されていたことの大きなトピックの一つにOptionalがありました。Optionalな型とはnilを代入できる型のことで、逆に言うとOptionalでない型にはnilを代入できません。

Objective-CのクラスはすべてOptionalでnilを代入できていました。それどころか、あるクラスの変数からメソッドを呼び出した際、その変数にnilが代入されている場合は何もしないという、nilかどうかの判定を実行時に毎回行う非常に動的な言語でした。

逆にSwiftではそもそもOptional型でないとnilを代入できません。最近のモダンな言語では一般的となった機能ですが、Swiftが世に出た当時はこの概念に慣れていないことから、多くの人がどのようにOptionalを扱うべきか議論を交わしていました。

Swift1.0の頃は、まだ実際にプロダクトをSwiftで開発する、という話はあまりありませんでした。その後、2015年にSwift2.0が発表され、多数の強力な文法が追加されました。特にProtocol Extensionは非常に優れた機能で、Appleがこの頃に提唱したProtocol Oriented Programmingという名称はあまり流行りませんでしたが、Swiftらしい実装をするためには欠かせない考え方といえます。

Protocol ExtensionとGenericsの組み合わせこそが、多重継承とtemplateのエッセンスを取り込んで発展させた機能といえるかもしれません。そして、この頃からObjective-CからSwiftへプロダクトを移行するケースが増えてきたと記憶しております。

なぜObjective-CではなくSwiftでアプリを作るのかというと、ひとえに最近のアプリケーションが重厚・複雑化してきているということに他なりません。

昔のiOSアプリケーションはシンプル・単機能であることが評価されましたが、スクリーンの大画面化・ユーザーの熟達に伴って、徐々にアプリケーションは複雑になっていきました。また、開発者が増加したことにより、シンプルで有用なアプリケーションはすぐに競合がひしめき合う状況となったのです。そもそもApple自体が優れたサードパーティの競合アプリと同じ用途のアプリを標準アプリとして追加するケースもありました。

こうした背景から、差別化のために容易には真似できない独自の機能を持たなければ、アプリが生き残れない時代になったのです。

アプリの機能と実装が複雑になっていくことで、プログラミング言語としては

  • シンプルで短く記述できる
  • 処理が高速である
  • 記述の誤りが実行時に分かる動的な言語ではなく、コンパイル時に判別できる静的な言語である
  • 共通の処理をクラスの継承関係に依存せず、目的の機能の単位で記述できる

ことが望まれるようになりました。

前述したOptionalやProtocol Extensionはこれらの特徴を強力にサポートしています。これが近年多くのアプリがSwiftで実装されている理由だと考えています。

Objective-CからSwiftへの移行戦略

Objective-CからSwiftへの移行と一言でいっても、アプリやサービスの状況、会社のリソース配分などによりさまざまな方法が検討されます。大まかには下記のように分類できるでしょう。

  • 一括リニューアル
  • 部分リニューアル
    • 共通処理を移行する
    • 共通処理を移行しない

一括リニューアルは既存のコードベースを全て捨てて完全に作り直す方法です。逆に部分リニューアルは既存のコードベースを残しながら画面をひとつずつ作り直す方法です。さらに、部分リニューアルについては、アプリの共通処理を作り直すかそうでないかで分類できます。これは2者択一になるものではなく、「一部の処理は残すが一部の処理は作り直す」といった決定がとられるケースが多いかと思います。

JP版メルカリでは部分リニューアルで徐々にリニューアルを進めており(社内ではre-architectureと呼んでいます)、現状は共通処理もほとんどは既存のコードベースのものを使用しています。

一般的なアプリの共通処理としては、APIクライアントやログ出力機能、ローカルDataBase、画像やAPIレスポンスのキャッシュ、初期データ用のマスタ、A/Bテストなどサーバーサイドからアプリの動作を制御する機能などが挙げられます。

私が以前勤務していたTOPLOGでも同様に部分リニューアルでObjective-CからSwiftへの移行を行いましたが、共通処理は移行しながらすべてSwiftで書き直していきました。

これはアプリの複雑性や、ログやA/Bテストの制御機構・初期データなどアプリエンジニアだけで決定できない要素が、JP版メルカリに比べてはるかに少なかったことが、共通処理を含めてリニューアルできた理由になります。

一括リニューアルのメリット&デメリット

一括リニューアルについて具体的に説明します。こちらは同じ機能を持った新しいアプリを作成することと同じです。メリットは既存のコードベースを全て破棄するため、技術的負債を一掃できるということです。また、この場合アプリのデザインやバックエンド側のコードも一新するケースが多いかと思います。

逆にデメリットとしては、一括リニューアルの期間中、既存のアプリの改善が完全にストップすることです。アプリの規模にもよりますが、数ヶ月間既存アプリの開発がストップするというのは経営的観点では大きな決断になるでしょう。

リニューアルチームと既存アプリの改善チームの2チームを並走させることがデメリットの解決策にも思えますが、これは非常に難しいと思います。改善チームの修正により、リニューアルチームの開発仕様が安定せず、非常にストレスの高い状態になりかねないからです。

また、一括リニューアルの段階では機能自体の絞り込みを行い、あまり使われていない機能をクローズするなどの決定が必要になるでしょう。それができない環境ならば、部分リニューアルを選択したほうが良いかと思います。

部分リニューアルで知っておきたいコツ

次に部分リニューアルについて説明します。こちらは画面を一つずつ中のコードを入れ替え、一つの画面が完成した段階でリリースしていく方法です。リニューアルをしながらでも別の画面では機能開発を継続できること、新しい画面を作る際にはリニューアルした構成で作成するなど、柔軟にプランを設定できることがメリットです。

逆にデメリットとしては、すべての画面がリニューアルされるまでに長い時間を要し、多くの場合古いコードすべてが置き換えられる時は永遠に訪れないことが挙げられます。つまり技術的負債が常に残り続けることを意味します。

部分リニューアルにおいて肝要なのは、デザインと機能はひとまず変更しないでおく、ということです。デザインと機能変更をリニューアルと同時に進行する問題点は、リニューアルした画面のリリースタイミングがそれに縛られることと、問題があった場合の切り戻しが容易にできなくなることです。

リニューアル中は同一機能のコードが2重になっているため、できるだけ小さく、できるだけ早くリリースしていくことが重要です。しかし、新機能やデザインの修正が入るとリニューアルした画面をすぐにリリースできず、同一機能のコードが2重管理となってしまいます。

また、リニューアルした画面に問題があった際にすぐに古い画面に戻せるようにしておくと良いでしょう。JP版メルカリではA/Bテストの機能を使い、サーバー側の設定変更で旧画面と新画面の入れ替えが可能な状態で開発を進めています。

一括リニューアルはObjective-CからSwiftへの移行というよりは、原則新しいアプリを作り直すことと同じ意味合いですので、本記事では以降、部分リニューアルについて解説していきます。

Objective-CからSwiftへの移行の4つのポイント

さて、Objective-CからSwiftへの移行する場合のポイントとして下記が大事だと考えています。

  1. 移行は画面単位で、ひとつの画面にObjective-CとSwiftを混在させない
  2. Objective-Cの機能を呼び出す場合はSwiftでWrapperを作成し、ビジネスロジックから直接Objective-Cに触れない
  3. BaseViewControllerを作成しない
  4. SwiftからObjective-Cを呼び出すことは問題ないが、Objective-CからSwiftを呼び出す場合には制限があることを理解する

1, 2のポイントはそれぞれObjective-CとSwiftをきちんと切り分けることが目的です。リニューアルの場合、基本的には言語のみならずアプリケーションのアーキテクチャ自体も最適なものを採用することが非常に重要で、Swiftの言語にマッチしたアーキテクチャを採用するのが良いでしょう。その場合、Objective-CとSwiftが混在する環境だとSwiftで最適化されたアーキテクチャが実装できません。

Objective-Cの機能呼び出しにSwiftでWrapperを作成する理由

2つ目のポイントについてもう少し詳細に説明します。例として、Objective-Cにおいてよく使用されているAFNetworkingという通信ライブラリを挙げます。

AFNetworkingは優れたライブラリであり、もちろんSwiftからも使用できますが、これをビジネスロジック内で直接使用してはいけません。AFNetworkingでAPIをコールする場合、通常レスポンスの型はJSONがデコードされてNSArrayとNSDictionayにマッピングしたid型(Swiftの場合はAny型)になります。

Objective-C時代のビジネスロジックでは、UIViewController内でこの中から個別に値を取得し展開する実装がよく見られました。これはObjective-Cが比較的動的な言語で、各要素の型を実行時に判定する書き方が容易なため頻繁に採用されていた手法です。

しかし、Swiftは実行時でなくコンパイル時に型を判定する静的な言語のため、こうした書き方が難しい上、コンパイル時に型を保証するSwiftの利点を潰してしまいます。

APIクライアントのロジックがシンプルならばAPIクライアントごとSwiftで書き直せばいいのですが、ロジックが複雑で書き直しが難しい場合があります。

その場合、まずSwiftで当該ロジックのWrapperを書くことをおすすめします。APIレスポンスの型をStructで定義し、Wapper部分でAPIリクエストの実行とレスポンスのオブジェクトからレスポンス型への変換を行います。オブジェクトからレスポンス型への変換はSwift4.0から追加されたCodableを使用しましょう。レスポンスの型をDecodableプロトコルに準拠させると、

let responce = try JSONDecoder().decode(Response.self, from: data)

と1行でJSONをデコードできます。また、APIクライアントの中で実際にAPIをコールしている部分は、APIKitを使用して書き直しても良いでしょう。これは、APIリクエストの型とレスポンスの型をマッピングしてくれるネットワーククライアントのライブラリです。

JP版メルカリの場合、APIレスポンスをキャッシュする機能などを実装している部分は現時点ではまだ一部Objective-Cのコードを利用していますが、APIをコールしている部分はSwiftでAPIKitを使用するように書き直しています。

つまりAPIクライアントの共通ライブラリとしてSwiftのコードがあり、Swiftへの移行がまだされていない処理が必要な場合はObjective-CのAPIクライアントを呼び出し、Objective-CからAPIKitを使用しているSwiftのコードを呼び出す形になっています。

BaseViewControllerを回避する理由

エンジニアHubに会員登録すると
続きをお読みいただけます(無料)。
登録のメリット
  • すべての過去記事を読める
  • 過去のウェビナー動画を
    視聴できる
  • 企業やエージェントから
    スカウトが届く