🪦

スペースマーケットiOSアプリの残Objective-Cコードを約3年かけてSwift化した話

に公開2

こんにちは、スペースマーケットでモバイルエンジニアをしている村田です。

先日のWWDC25「Liquid Glass」の発表で世のiOSエンジニアが沸く中、我々のエンジニアチームは2025年6月16日ついにObjective-Cの撲滅を完了し、盛り上がっていました!

昨今Objective-Cのコードが残っているプロジェクトは少ないかもしれませんが、入社してから約3年かけて取り組んできたSwift化の歩みを紹介することで、同じように移行に取り組んでいる方の励みや参考になれば嬉しいです。

スペースマーケットゲストiOSアプリの歴史

提供開始

10年前の2015年6月30日、スペースマーケットのゲストアプリがストアに掲載されたようです。最初のアプリはフルObjective-C製でした(プレスリリース

Swift導入

過去のPRを遡ったところ、2015年11月にリリースされたバージョン1.2.1に含まれていたApple Watch対応の実装が、Swiftコード導入の第一歩だったようです。
つまり、Objective-C撲滅は10年に渡る戦いだったということです。歴代のiOSエンジニアが受け継いできたSwift移行のバトンを、自分の代で完遂できたことは、とても光栄に思っています。

https://www.macotakara.jp/WatchApp/entry-28395.html

移行推移

以下はObjective-Cの利用率推移のグラフになります。2016年から2019年にかけて大幅に進んだようで、30%近くまで削減されていました。

進捗の良い2016年から2017年にかけての対応PRを確認したところ、デザインリニューアル/新規画面作成に伴いSwift化が進んでいるようでした。実際私が入社した2022年にはPresentation層に関するコード(ViewControllerなど)はSwift化が完了している状態でした。

2020年頃からやや停滞しており、AppDelegateや通信処理、Model(Entity)など、手を加えると影響範囲が広く、動作確認にも時間を要するラスボスのようなコードばかり残っていたことが原因のようです(それを現エンジニアが対応することに)

Objective-C残コードSwift化への道

入社当初状況

私が入社した2022年2月の言語比率は以下の通りで、Objective-Cコードが約12%残っている状況でした。

入社時にはiOSエンジニアの先輩が2人在籍していましたが、入社から2ヶ月後にはお二人とも退職され、iOSエンジニアは自分1人の体制となりました。そのため、こうした改善活動に十分な時間を割くことが難しい時期がしばらく続いていました。

---------------------------------------------------------------------------
Language                 files          blank        comment           code
---------------------------------------------------------------------------
JSON                       348              0              0         119214
Swift                      444          12272           8740          55041
XML                        262            865            122          38633
Objective-C                 63           1303            875           5792
C/C++ Header                68            606            984           1683
GraphQL                     21             61             81           1299
---------------------------------------------------------------------------

苦しかったポイント

Objective-C製/Swift製/GraphQLフラグメントの3種類のModel(Entity)が混在しており、Swift製のModelを中間として変換を行った上で画面やコンポーネントへ受け渡す必要があり、開発効率が非常に悪い状況でした。
Objective-Cの削除を終わらせないとずっとこのストレスを抱えながら開発しないといけないのか...と考えると、優先度高く撲滅を進めないといけないなと感じていました。

以下はRoom情報に関するModelを例とした変換のイメージです。

// SMRoom.h

#import "SMModel.h"
#import "SMRoomSystemConnect.h"

@protocol SMRoom;

@interface SMRoom : SMModel
@property (nonatomic, strong, nullable) NSNumber *model_id;
@property (nonatomic, strong, nonnull) NSString *uid;
@property (nonatomic, strong, nullable) NSString *name;@property (nonatomic, strong, nullable) NSDictionary *space;
@property (nonatomic, strong, nullable) NSDictionary *owner;

+ (nonnull SMRoom *)initWithDic:(nonnull NSDictionary*)dic;

@end

// SMRoom.m

#import "SMRoom.h"
#import "NSDictionary+Extensions.h"

@implementation SMRoom

+ (SMRoom *)initWithDic:(NSDictionary*)dic {
    SMRoom *room = [[SMRoom alloc] init];
    room.model_id = [dic objectOrNilForKey:@"id"];
    room.uid = [dic objectOrNilForKey:@"uid"];
    room.name = [dic objectOrNilForKey:@"name"];
    ︙
    room.owner = [dic objectOrNilForKey:@"owner"];
    room.space = [dic objectOrNilForKey:@"space"];
    return room;
}

@end
// Room.swift

struct Room: Codable {
   let id: Int?
   let uid: String
   let name: String?let owner: Owner?
   let space: Space?

   private enum CodingKeys: String, CodingKey {
      case id
      case uid
      case name
      ︙
      case owner
      case space
   }
}

extension Room {
   // Objective-Cへの変換
   func toSMRoom() -> SMRoom? {
      let dictionary = self.toDictionary()
      return SMRoom.initWithDic(dictionary)
   }
}

extension SMRoom {
   // Objective-Cからの変換
   func toSwift() -> Room {
      return Room(
         id: model_id?.intValue,
         uid: uid,
         name: name,
         ︙
         owner: self.makeOwnerSwift(),
         space: self.makeSpaceSwift()
      )
   }
}

移行が進んだ対応

振り返ると以下3件の対応がフルSwift化へ大きく貢献したため紹介させてください。

2023年: iOS17不具合によるJsonModelの削除

RESTリクエストで取得したJSONデータからObjective-C製Modelへの変換ライブラリとして、JSONModel を使用していました。iOS17端末にてJSONModel起因で予約詳細画面に不具合が発生し、依存し続けることに不安があるねという話になり、スクラムで正式に課題として取り上げ剥がすことになりました。

詳細は以下の記事にまとめていますので、興味のある方はご一読いただけると嬉しいです。

https://zenn.dev/spacemarket/articles/goodbye-jsonmodel

2023年: Privacy Manifest対応

Objective-C製RESTクライアントの通信処理はAlamofireの前進ライブラリであるAFNetworkingに依存していました。

AFNetworkingはPrivacy Manifest対応の対象だったのですが、Alamofireのissueにて、開発者から対応しませんよとバッサリコメントがあったため(そもそもメンテナンス終了しているので当たり前ですが)、Alamofireへ移行するか剥がして内製化する必要がありました。

Swift製のRESTクライアントクラスは作成済みだったため、旧クライアントクラスを利用しているAPIリクエスト処理を全てそちらに寄せることで、AFNetworkingと旧クラスを削除することができました🎉 ライブラリ自体が下手にPrivacy Manifest対応してくれていたらそちらに倒れていそうだったので、むしろノー対応の選択をとってくれた事に感謝です。

2024年: 認証方法の移行

バックエンドはOAuth認証認可のライブラリとしてdoorkeeperを利用しており、Railsバージョン更新の足枷となっていました。こちら開発チームとして温度感高く剥がす対応しましょうという話になり、まずアプリが認証方法移行しないといけないということで、ゲストアプリ(iOS/Android)/ホストアプリ(Flutter)共にカスタムトークン認証への移行対応を進めました。

iOSアプリは認証周りの実装がOAuth2ClientをベースとしてObjective-Cで組まれており、気軽に剥がすのが困難な状態でした。ミスが発生したらユーザー影響が大きく、個人の時間で進めるのは難しかったため、LeSSチームとして優先度高で対応しましょうと判断してくれたPOに感謝しています。

リリース後暫く問題なかったのですが新しい端末へ機種変更するユーザーが増える時期に、ログインができないという問い合わせが増えました。
以下記事に記載の通り、Firebase認証情報とUserDefaults保存データの掛け合わせでログイン状況を判断していたため、思わぬバグを生んでしまいました。皆さんもお気をつけください🙏

バックアップの復元先が元の端末かそうでないかでFirebaseの認証情報が復元されるかどうかが変わってきます。
特にUserDefaultsやキーチェーンなど、復元される項目と組み合わせて情報を組み立てている場合、バグの温床となります。

https://qiita.com/hyuga_amazia/items/2fb58690a4064ac7a66e

2025年ラストスパート

2025年1月時点で、プロジェクト内のObjective-Cの残存コードは全体の1.7%となり、ようやく終わりが見え始めていました。このまま定期的な削除活動を続けていけば、上期中には完了できそうだという手応えがあり、ワクワク感がありました。

iOSエンジニア朝活

移行スピードはその時々の自分のやる気に依存してしまっており、あまり健全じゃない状態でした。メンバーと相談し、デイリースクラム前の1時間確保し、iOS品質改善活動を行うことにしました(Objective-C撲滅が完了したため、今はCocoaPodsからSwiftPackageManagerへの移行を進めています)

新入社員の活躍

4月に新入社員の細田さんがアプリエンジニアとしてチームに加わりました。
昨年からインターンとしてiOS/Android/バックエンドの学習や、ドメイン知識の吸収を積み重ねていたため、入社後すぐにタスクをスムーズに任せられる状態でした。

正直、新卒の方にSwift移行のタスクを渡しても楽しくないだろうな...と思っていたのですが、ガッツとやる気にあふれた方で「一緒に消しましょう!」とノリノリで引き受けてくれました。嬉しい。
結果、Objective-C製のRESTリクエストを10本程度Swiftへ移行してくれました!大感謝です。

細田さんのエントリー記事、ぜひご一読ください🙏

https://note.com/hip_flax9912/n/n50b819ac9d8e

終わりと始まり

こうした草の根的な活動はなかなか評価されにくく、「やって当たり前」とされがちですが、自分の上司は細かいところまで見てくれて、しっかり評価してくれるのでとても感謝しています!

飽きずに継続できたのもスペースマーケットの「チャレンジを応援する」文化があったからこそだと思います。今後もiOSのみに縛られず改善活動進めていくつもりです💪

振り返り

正直入社当時は終わりが見えず、3年で終わるとは思っていませんでした。

UIKit製画面のSwiftUI化を進めるなど楽しい作業を後回しにし、日頃この対応にリソースを割くことは自分の我儘だったかもしれません。文句言わずレビューや分割したタスクの対応をしてくれたアプリメンバーにはとても感謝しています!

不要ライブラリ削除、中途半端に導入されたアーキテクチャ(Flux)削除、Objective-Cの削除...。と、魅上照ばりに削除を繰り返しプロジェクトの内部品質改善に時間を割いてきました。
足場が整ってきたこの状況で、今後は気持ちよくUI/UXの改善を進め、ユーザーの皆さんに使い勝手の良いプロダクトをお届けできればと思っています。

最後に

スペースマーケットでは一緒に働く仲間を絶賛募集中です!
詳しくは以下をご確認の上ご応募ください。

https://jobs.forkwell.com/spacemarket/jobs/28583

GitHubで編集を提案
スペースマーケット Engineer Blog

Discussion

わたなおわたなお

PRのタイトルもコメントも内容が最高すぎる!!!
いい会社ですね、楽しそう。

記事に書いていることもお疲れ様でした。 👍

nmuratanmurata

コメントありがとうございます!
今更Objective-C関連の記事なんて...と思っていましたが、
社外の方にそう言っていただけると執筆して良かったなと救われました😭