概要
9月1日からMIXIのインターンシップ「Dive Into MIXI」のminimo事業部プロダクト開発グループアプリ負債返済チームで働きました!
SwiftUIで状態管理をしていると、@Published、@StateObject、@ObservedObject、をたくさん目にすると思います。でも「どれが Combine?どれが SwiftUI?」「結局どう違うの?」と混乱しがちです。この記事ではそれぞれの役割を整理します。
対象者
- SwiftUI を触り始めて「状態管理ワード」がごちゃごちゃに見える人
- Combine の「発行と購読」という言葉は知っているけど、実際どうSwiftUIと関係するのか曖昧な人
- 業務コードで
assign(to:)や$error.map { … }を見かけて「これもCombine?」と思ったことがある人
私について
現在、インターンでiOS開発を行なっています。
個人開発のときは「なんとなく使えていればOK」くらいで済んでいましたが、業務になるとそうはいきません。なぜこのコードを書くのかを明確に説明できなければ、PRレビューの嵐の中で突っ込まれます。
そこで直面したのが、SwiftUIの状態管理まわりのキーワードたち。
-
@Publishedと@StateObject / @ObservedObjectは全部Combineなの? -
ObservableObjectって結局何者? -
.assign(to:)ってCombine?SwiftUI?
最初はフワッとした理解でやり過ごしていましたが、業務で本格的に触る中で「これはちゃんと整理しないとやばい」と痛感しました。
この記事は、その学びをまとめたものです。
Combineとは?
Combine は Apple が提供する汎用的なリアクティブフレームワーク です。
値の変化をイベントとして発行し、それを宣言的に処理する仕組みを提供しています。
- SwiftUI だけでなく、UIKitやCLIアプリでも使えます。
- ただし SwiftUI の状態管理(
@Published,ObservableObjectなど)は内部的に Combine を利用しているため、SwiftUI に触れていると自然に目にすることが多いです。
Combine の流れはシンプルで、
- 値の変化を Publisher(発行者) が発行
- 必要に応じて Operator(演算子) が変換・加工
- Subscriber(購読者) が受け取って処理
となります。
これはCombineなの?
業務でこんなコードに出会いました。実はこれもCombineの書き方です。
$error
.map { $0 != nil ? .failed : .idle }
.receive(on: DispatchQueue.main)
.assign(to: &$viewState)
流れ(Publisher → Operator → Operator → Subscriber)
-
Publisher (
$error)-
@Published var errorがあると、自動でerrorの Publisher($error)が生成される - 「error が変わったらイベントを発行」
-
-
Operator (
.map { ... })- 受け取った
errorを.failed/.idleに変換
- 受け取った
-
Subscriber (
.assign(to:))- 最終的な値を
viewStateプロパティへ代入(&$viewStateは@Published 付きプロパティへ書き込む)
- 最終的な値を
図にするとこんな感じです。
用語集
これがCombine?じゃあ、@Published / @StateObject / @ObservedObject は何?となったので、表にまとめました。
| 仕組み / 記法 | レイヤー | 何をする? | 補足 |
|---|---|---|---|
@MainActor / actor / async・await
|
Swift 言語 / Concurrency | スレッド安全・並行処理を言語レベルで保証 | Combine / SwiftUI とは独立した基盤 |
Publisher / Subscriber / @Published / PassthroughSubject / .sink
|
Combine | 値のストリームを宣言的に処理 | UI不要で利用可能。SwiftUI(〜iOS16) 内部でも利用 |
ObservableObject + @Published / @StateObject / @ObservedObject / @EnvironmentObject
|
SwiftUI (〜iOS16) | 値変化を監視し、Viewを再描画 |
@PublishedはCombine。SwiftUIがそれを購読してUI更新 |
@Observable / @State
|
SwiftUI (iOS17〜) | フィールドアクセスを自動トラッキングしてViewを再描画 | Combine不要。Swift 5.9+ / iOS 17+ |
なんで Combine 記事なのに @StateObject / @ObservedObject が出てこないことがあるの?
-
純粋に Combine を説明している記事は、UI(SwiftUI)を絡めないので
@StateObject/@ObservedObjectは登場しません - 代わりに
PassthroughSubject,.map,.filter,.debounce,.sink,.assignなど、Publisher/Operator/Subscriber の話が中心になります -
SwiftUI × Combine を説明する記事では、
ObservableObjectと@Publishedに加えて@StateObject/@ObservedObjectが登場します
ここまでの整理
-
@Published は Combine
値の変更をイベントとして発行する仕組み。
→ UI に依存せず、バックグラウンド処理や CLI アプリでも使える -
ObservableObject は SwiftUI への橋渡し
Combine の発行を SwiftUI が監視できるようにするためのプロトコル
→ObservableObjectに準拠しないと、UI の再描画は走らない -
@StateObject / @ObservedObject は SwiftUI の購読手段
-
@StateObject: View がインスタンスを「所有」して監視する -
@ObservedObject: 外から渡されたインスタンスを「監視」するだけ
→ どちらも UI 更新専用 の仕組み
-
-
まとめると
-
@Published: Combine 側の「発行」 -
ObservableObject: SwiftUI がそれを購読できるようにする「橋」 -
@StateObject / @ObservedObject: SwiftUI 側での「購読」
-
まとめ
-
Combine は汎用フレームワーク
SwiftUI 専用ではなく、UIKit や CLI アプリでも使える「値の発行と購読の仕組み」。
-
@Published は Combine
値の変化を Publisher として発行する。UI 限定ではなく、バックエンド処理や非UIコードでも利用可能。
-
ObservableObject は橋渡し
Combine の発行を SwiftUI が監視して、View を再描画できるようにするためのプロトコル。
-
@StateObject / @ObservedObject は SwiftUI 専用の購読手段
-
@StateObject: View がそのオブジェクトを所有するとき -
@ObservedObject: 外部から渡されたオブジェクトを監視するとき→ Combine の Publisher を SwiftUI が購読して UI を更新している。
-
おわりに
Observationフレームワークの知見を深めたいです。
インターン頑張ります!
