SwiftUIでアプリを開発していると、
- 「商品をカートに追加した時に、ナビゲーションバーのカートアイコンも同時に更新したい」
- 「ユーザーがログインした瞬間に、複数の画面で表示を変更したい」
といった場面に遭遇することがあります。
このような「離れた場所にあるコンポーネント同士で情報をやり取りしたい」という課題を解決してくれるのが、NotificationCenter.default.postです。
この記事では、NotificationCenter.default.postの基本的な仕組みから実際の使用方法、指定できるオプションまで、わかりやすく解説していきます。
NotificationCenter.default.postとは
NotificationCenter.default.postは、Swiftでアプリ内の異なる部分間でメッセージを送信するための仕組みです。
これは「通知」を送信する機能で、プッシュ通知とは全く別のものです。
簡単に言えば、アプリ内での「お知らせシステム」のようなもので。
ある画面やクラスから別の画面やクラスに対して「○○が起きました」という情報を伝えることができます。
基本的な使い方
NotificationCenterを使うには、「送信側」と「受信側」の2つの設定が必要です。
まるで無線機のように、一方が「送信」し、もう一方が「受信」する仕組みです。
1. 通知を送信する(post)
まずは通知を送信する方法から見てみましょう。
NotificationCenter.default.postを使って、アプリ内に「○○が起きました」というお知らせを送信できます。
| 1 2 3 4 5 6 7 8 9 10 11 | // 基本的な通知の送信 NotificationCenter.default.post(name: Notification.Name("UserLoggedIn"), object: nil) // カスタム通知名を定義して使用 extension Notification.Name {     static let userLoggedIn = Notification.Name("UserLoggedIn")     static let dataUpdated = Notification.Name("DataUpdated") } NotificationCenter.default.post(name: .userLoggedIn, object: nil) | 
ポイント:
- 通知には必ず「名前」をつけます。
- この名前が「どんな種類のお知らせか」を識別するための目印になります。
2. 通知を受信する(観察者として登録)
次に、送信された通知を受信する方法です。
SwiftUIではonReceiveを使って、特定の通知が来るのを「待ち受ける」ことができます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import SwiftUI struct ContentView: View {     @State private var isLoggedIn = false     var body: some View {         VStack {             Text(isLoggedIn ? "ログイン済み" : "ログアウト中")             Button("ログイン") {                 // 通知を送信                 NotificationCenter.default.post(name: .userLoggedIn, object: nil)             }         }         .onReceive(NotificationCenter.default.publisher(for: .userLoggedIn)) { _ in             // 通知を受信した時の処理             isLoggedIn = true         }     } } | 
ポイント:
- onReceiveで特定の通知名を指定して「この通知が来たらこの処理を実行してください」と設定します。
- 通知が送信された瞬間に、受信側の処理が自動的に実行されます。
postメソッドのオプション
NotificationCenter.default.postには以下のパラメータを指定できます:
1. name(必須)
通知の名前を指定します。
この名前で通知を識別します(下記の場合は MyNotification が通知の名前)
| 1 2 | NotificationCenter.default.post(name: Notification.Name("MyNotification"), object: nil) | 
2. object(オプション)
通知の送信者や関連するオブジェクトを指定します。
「誰がこの通知を送信したのか」を受信側に伝えたい場合に使用します。
| 1 2 3 4 5 6 7 8 9 | class UserManager {     func login() {         NotificationCenter.default.post(             name: .userLoggedIn,             object: self  // 送信者として自分自身を指定         )     } } | 
objectを設定することで、複数の画面から同じ通知が送信される可能性がある場合、受信側で「どこから送られてきた通知なのか」を判別できます。
ただし、多くの場合は<code">nilのままで問題ありません。
3. userInfo(オプション)
通知と一緒に追加の情報を送信できます。
辞書形式でデータを渡せるため、「ログインしました」だけでなく「誰がログインしたのか」「いつログインしたのか」といった詳細情報も一緒に送れます。
| 1 2 3 4 5 6 7 8 9 10 11 | // データと一緒に通知を送信 NotificationCenter.default.post(     name: .dataUpdated,     object: nil,     userInfo: [         "userId": 12345,         "userName": "田中太郎",         "timestamp": Date()     ] ) | 
userInfoは[String: Any]形式の辞書です。
キー(文字列)と値(任意の型)のペアでデータを送信できます。
userInfoを受信する例
受信側では、送信されたuserInfoからデータを取り出して利用できます。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | struct ProfileView: View {     @State private var userName = ""     var body: some View {         Text("ユーザー: \(userName)")             .onReceive(NotificationCenter.default.publisher(for: .dataUpdated)) { notification in                 // userInfoからデータを取得                 if let userInfo = notification.userInfo,                    let name = userInfo["userName"] as? String {                     userName = name                 }             }     } } | 
コードの解説:
- notification.userInfo- 通知に含まれるuserInfoの辞書を取得
- userInfo["userName"]- 辞書から"userName"キーの値を取得
- as? String- 取得した値をString型にキャスト(安全にキャスト)
userInfoの値はAny型なので、使用する前に適切な型(String、Int等)にキャストする必要があります。
実践的な使用例
どんな場面で使うの?
NotificationCenter.default.postは、以下のような「離れた場所にある画面やコンポーネント同士で情報を共有したい」場面で威力を発揮します。
- 商品をカートに追加した時に、画面上部のカートアイコンの数字を更新したい
- ユーザーがログインした時に、複数の画面で表示を変更したい
- 設定画面で変更した内容を、他の画面にも即座に反映させたい
- データの更新があった時に、関連する全ての画面を更新したい
ショッピングカートの例で理解しよう
やりたいこと: ECアプリで、商品詳細画面から「カートに追加」ボタンを押した時に、画面上部のカートアイコンに表示される商品数のバッジを即座に更新したい。
問題: 商品詳細画面(深い階層)とカートアイコン(ナビゲーションバー)は全く別々の場所にあるため、直接的にデータを渡すのが困難。
NotificationCenter.default.postによる解決:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | extension Notification.Name {     static let cartUpdated = Notification.Name("CartUpdated") } // 商品をカートに追加する処理(商品詳細画面など) class CartManager: ObservableObject {     @Published var itemCount = 0     func addItem() {         itemCount += 1         // ここがポイント!カート更新の通知を送信         // アプリ全体に「カートが更新されました」というお知らせを送る         NotificationCenter.default.post(             name: .cartUpdated,             object: nil,             userInfo: ["itemCount": itemCount] // 商品数も一緒に送信         )     } } // カートのアイコンを表示するView(ナビゲーションバーなど) struct CartIconView: View {     @State private var badgeCount = 0     var body: some View {         ZStack {             Image(systemName: "cart")             if badgeCount > 0 {                 Text("\(badgeCount)")                     .foregroundColor(.white)                     .background(Color.red)                     .clipShape(Circle())             }         }         // ここがポイント!カート更新の通知を待ち受け         .onReceive(NotificationCenter.default.publisher(for: .cartUpdated)) { notification in             // 通知が来たら、送られてきた商品数でバッジを更新             if let userInfo = notification.userInfo,                let count = userInfo["itemCount"] as? Int {                 badgeCount = count             }         }     } } | 
どう実現しているか:
- 送信側(CartManager): 商品がカートに追加された瞬間にNotificationCenter.default.postでアプリ全体に「カートが更新された」という通知を送信
- 受信側(CartIconView): onReceiveで常にカート更新の通知を待ち受けており、通知が届いたら即座にバッジの数字を更新
- データの受け渡し: userInfoを使って具体的な商品数も一緒に送信するため、受信側は正確な数字を表示できる
この方法により、どんなに離れた場所にあるコンポーネント同士でも、リアルタイムで情報を共有できます。
まとめ
NotificationCenter.default.postは、アプリ内での効率的な情報共有を可能にする強力な仕組みです。
SwiftUIのonReceiveと組み合わせることで、リアクティブなUIの更新も簡単に実現できるので、ぜひ使ってみてください!