Slide 1

Slide 1 text

© DMM.com CONFIDENTIAL DMMオンラインサロン アプリのSwift化 Sansan x DMM.swift DMM.com オンラインサロン開発部 大門弘明 2025/01/17

Slide 2

Slide 2 text

© DMM.com 大門弘明 合同会社DMM.com デジタルコンテンツ開発本部 オンラインサロン開発部 iOSエンジニア 新卒入社からはや10年以上 アニメやゲームが好きな引きこもり 昨年はこの歳になって知人にパチスロを教わり脳 を焼かれた模様 自己紹介 2

Slide 3

Slide 3 text

© DMM.com

Slide 4

Slide 4 text

© DMM.com

Slide 5

Slide 5 text

© DMM.com 過去の登壇 オンラインサロンのメンバーは関連テーマで何度か登壇している 5

Slide 6

Slide 6 text

© DMM.com 今回の内容 • オンラインサロンアプリのSwift化ので道のりを振り返る • SwiftUIアプリでのRedux採用を紹介 6

Slide 7

Slide 7 text

© DMM.com オンラインサロンアプリと は? 7

Slide 8

Slide 8 text

© DMM.com

Slide 9

Slide 9 text

© DMM.com オンラインサロンアプリとは? 9 オンラインサロンはクローズドな会員制コミュニティサービス タイムライン 投稿・コメント・返信など コミュニティの柱となる機能群 ライブ配信視聴 サロンオーナー(主宰者)が 行っているライブ配信の視聴 Push通知 コミュニティの動向を リアルタイムで受け取る サロン会員専用のiOSアプリ 主な機能はSNSに似ておりそこそこの規模のアプリ(45画面以上)

Slide 10

Slide 10 text

© DMM.com プロジェクトの背景 10

Slide 11

Slide 11 text

© DMM.com Swift移行前のアプリ 11 フロントエンドエンジニア主流でスピード開発を行った経緯がある アップデート Xcode / ReactNative / npm 等 影響範囲が複雑すぎて 手に負えない 採用 ReactNativeに精通した iOSエンジニア採用の難しさ メリットの薄さ iOSのみ提供しており ReactNativeの メリットが薄かった WebはReact, iOSはReactNativeを採用 ReactNativeのメリットよりも課題が大きくなってしまっていた

Slide 12

Slide 12 text

© DMM.com Swift化 12 アプリ内課金対応が必要になり同時にSwift化に踏み切る メンバーや社内に知見が豊富だったため、当初はVIPERを採用 アプリエンジニア主導で本領発揮できる開発環境に移行していく

Slide 13

Slide 13 text

© DMM.com VIPERの課題 なぜ途中からReduxの採用に舵を切り直したのか 13 画面毎の状態管理で、別画面の処理の結果を反映させるのが複雑 Reduxで実装されていた機能や処理の流れを再設計する必要がある → 仕様漏れやデグレが起こりやすくなっていた

Slide 14

Slide 14 text

© DMM.com Reduxにすることで期待した効果 14 既存の設計をなるべき流用しつつ、SwiftUIアプリにする 宣言的UIに適したSingle Source of Truthな状態管理を実現する

Slide 15

Slide 15 text

© DMM.com 現状のサービスを保守運用しながら少人数での開発 15 私(リード) + ミドル1名 + ジュニア1名 Swift化以外にもタスクがある ● サロンオーナー向けライブ配信アプリの保守 ● VIPER+ReactNativeで一部Swift化していたアプリの保守 旧アプリとほぼ同じUI/機能を再開発

Slide 16

Slide 16 text

© DMM.com スケジュール感 16 2021 2022 2023 2024 2025 アプリ内課金 Swift化(VIPER / ReactNative共存) Swift化(Redux) 新機能実装 リファレンスアプ リ作成 機能移植 このあたりでVIPERに 限界を感じ始める 2024年11月 リリース ・クラッシュ ・デグレ ・スピード感

Slide 17

Slide 17 text

© DMM.com ReduxをSwiftUIで どう実装したか 本題かもしれません 17

Slide 18

Slide 18 text

© DMM.com Reduxとは何か 18 ReactJSが扱うUIの状態管理をするためのライブラリ ReduxはFluxの概念を拡張した設計 つまり厳密にはオンラインサロンアプリはReduxではなくFluxライクな状態管理である。 仕組みとしてはRedux+Sagaをほぼそのまま取り入れている。 Web版やReactNative版のアプリにあわせてそう呼称している。

Slide 19

Slide 19 text

© DMM.com Reduxとは何か 19 Action / Store / State / Reducer で構成される アプリケーションの状態管理を一元化し予測可能な動作を保証する Actionが発火 → 一元化された状態(State)を Reducerで更新する UIはStateを常に反映する

Slide 20

Slide 20 text

© DMM.com オンラインサロンアプリのReduxの構成要素 20 Store, Dispatch Action Reducer Saga (Middleware) → Actionに応じた非同期処理を実現するために、 Redux+Sagaのような仕組みを実装している SelectState と UseDispatch → SwiftUIにReduxのStateを反映させるためのオンラインサロンアプリの独 自の仕組み

Slide 21

Slide 21 text

© DMM.com Store, Dispatch 21 アプリの根幹部分はこの40行弱のコード @Published public var state: StoreState で SwiftUIに伝達可能な状態を保持している。 アクションが発火すると dispatch(action: Action)が 呼び出され、Reducerを実行し、新しい状態に更新 する。

Slide 22

Slide 22 text

© DMM.com Action 22 ActionはSendableなPayloadを持っている Swift Concurrency で処理するため、Sendableに準 拠するようにしています。 右の例は投稿の一覧を取得するアクションです。

Slide 23

Slide 23 text

© DMM.com Reducer 23 StateとActionを引数にとり、新しいStateを返す単純な typealias 機能ごとにReducerはいくつも存在しますが、 最終的に appReducer(_:, _:) という関数に集約され るようになっています。 複数のReducerを一つのReducerにまとめる処理もある

Slide 24

Slide 24 text

© DMM.com Reducer 24 appReducer(_:, _:) をちょい出しするとこんな感じです。

Slide 25

Slide 25 text

© DMM.com Saga (Middleware) 25 Actionが発火したときに非同期で処理を呼び出す仕組み APIの呼び出しや、データの更新などはこれを経由して 実行されます。 例えば、 FetchPosts というアクションが発火したときには APIから投稿のデータを取得し、取得できたことを示す FetchPostsSuccess というアクションを発火するとい う処理が実装されています。

Slide 26

Slide 26 text

© DMM.com SelectState と UseDispatch 26 オンラインサロンアプリのカスタムPropertyWrapper SwiftUIにStateを反映する → SelectState SwiftUIからアクションを発火させる → UseDispatch

Slide 27

Slide 27 text

© DMM.com SelectState 27 SwiftUIにStateを反映するPropertyWrapper @SelectState(\AppState.salonsState) private var salonsState のように宣言すると、 SwiftUIの @Stateみたいな感じで利用できます。

Slide 28

Slide 28 text

© DMM.com UseDispatch 28 SwiftUIからアクションを発火させるPropertyWrapper @UseDispatch(AppState.self, TimelineActions.self) private var useTimeline のように宣言すると、 SwiftUIの task() や ボタンのアクションで次のようにアクションを発 火させることができます。 useTimeline.dispatch(.fetchPosts)

Slide 29

Slide 29 text

© DMM.com 現行の実装とメリット 29 状態の更新がViewに即座に反映される → WebSocketでリアルタイムに状態が更新されるが、 UI側の実装ではそれを意識する必要は無い 共通処理の一元化 → 同じ情報(いいねなど)を画面毎に取得・更新する必要は無い 処理順序が概ね旧アプリと同じ → 移行時のロジック考慮漏れなどを減らす

Slide 30

Slide 30 text

© DMM.com 現状の課題 30 機能を実現するための処理手順などを再設計していない部分がほとんどな ので、ベストではない部分が所々ある パフォーマンスの課題 → UI更新時のパフォーマンスに影響を与えていそうな処理が存在する → メモ化等で対応を検討

Slide 31

Slide 31 text

© DMM.com さいごに 31

Slide 32

Slide 32 text

© DMM.com 今後の取り組み 32 今後は機能追加やグロースをメインに行っていく パフォーマンス監視と最適化の取り組み XcodeOrganizerやFirebaseを利用してパフォーマンス監視とその原因の模索を定期的を行っている。 すでにいくつかのパフォーマンスに影響を与えていそうなコードや設計を特定。 機会があったらお話しすることもあるかもしれません。

Slide 33

Slide 33 text

© DMM.com 採用 33 グロースやパフォーマンス改善を一緒に進める仲間を募集しています。 事業を成長させることに興味があるかたはぜひ! 採用HPのiOSエンジニア(オープンポジション)の募集から、 サロンに興味ある旨をお伝えください。

Slide 34

Slide 34 text

© DMM.com おわり