Using NotificationCenter in SwiftUI to send data between different parts of an app
Advanced SwiftUI, SwiftUI Advanced Swift, NotificationCenter, Swift, SwiftUI, XCodeIn iOS development, NotificationCenter
is often used to send information between different parts of an app. Here, we’ll look at how to use it in Swift to notify different views of a successful payment, using a real-world example in SwiftUI.
In this example, we have two main SwiftUI views:
BookingView
– Simulates a payment process and sends a success notification.ExploreView
– Listens for the payment success notification and displays an alert with the reservation ID.
Let’s walk through each piece of the code and explain how it works.
Setting Up the NotificationCenter Extension
To make our code cleaner, we extend NotificationCenter
to define custom notifications with specific user info keys. This makes it easy to handle different notification types and access the associated information.
Here’s the code for the NotificationCenter
extension:
import Foundation
extension NotificationCenter {
class Name {
class ReserveASeatAction: NotificationCentersProtocol {
enum userInfoKeys: UserInfoKeysProtocol {
case success
case reservationId
var getString: String {
switch self {
case .success: return "success"
case .reservationId: return "reservationId"
}
}
}
}
}
}
protocol NotificationCentersProtocol {
associatedtype userInfoKeys: UserInfoKeysProtocol
}
extension NotificationCentersProtocol {
static func notificationName() -> NSNotification.Name {
return Notification.Name(String(describing: self))
}
}
protocol UserInfoKeysProtocol: Codable, CaseIterable {
var getString: String { get }
}
Explanation:
NotificationCenter.Name
: This class defines notification names.ReserveASeatAction
is a custom notification name related to seat reservations.userInfoKeys
: These are keys used in the notification’s userInfo dictionary. They store values such assuccess
(a boolean indicating if the payment was successful) andreservationId
(the unique ID for the reservation).getString
: This property returns the string value for each key, so we can use these keys easily inuserInfo
.
Implementing BookingView
In BookingView
, the user initiates a payment process. Once the payment is processed, a success
notification is sent.
import SwiftUI
struct BookingView: View {
@State private var isProcessingPayment = false
@State private var reservationId = "12345" // Mock reservation ID for this example
@Environment(\.dismiss) var dismiss
var body: some View {
VStack(spacing: 20) {
Text("Booking Page")
.font(.largeTitle)
.padding()
Text("Reservation ID: \(reservationId)")
.font(.headline)
if isProcessingPayment {
ProgressView("Processing Payment...")
.padding()
} else {
Button("Pay Now") {
processPayment()
}
.buttonStyle(.borderedProminent)
.padding()
}
}
}
private func processPayment() {
isProcessingPayment = true
// Mocking a payment delay
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
isProcessingPayment = false
postPaymentSuccessNotification()
dismiss()
}
}
private func postPaymentSuccessNotification() {
NotificationCenter.default.post(
name: NotificationCenter.Name.ReserveASeatAction.notificationName(),
object: nil,
userInfo: [
NotificationCenter.Name.ReserveASeatAction.userInfoKeys.success.getString: true,
NotificationCenter.Name.ReserveASeatAction.userInfoKeys.reservationId.getString: reservationId
]
)
}
}
Explanation:
Payment Process
: When the “Pay Now
” button is clicked,processPayment
is called. This simulates a delay and then posts a success notification with the reservation ID in userInfo.postPaymentSuccessNotification
: This method posts a notification with the nameReserveASeatAction.notificationName()
and includes the reservation ID and success status in userInfo.
Listening for the Notification in ExploreView
Now, let’s set up ExploreView
to listen for this notification. When it receives the success notification, it shows an alert displaying the reservation ID.
import SwiftUI
struct ExploreView: View {
@State private var showAlert = false
@State private var reservationId: String?
var body: some View {
VStack {
Text("Explore View")
.font(.title)
.padding()
// Button for navigation or other actions can go here
}
.onReceive(NotificationCenter.default.publisher(for: NotificationCenter.Name.ReserveASeatAction.notificationName())) { info in
if let obj = info.userInfo,
let success = obj[NotificationCenter.Name.ReserveASeatAction.userInfoKeys.success.getString] as? Bool,
let id = obj[NotificationCenter.Name.ReserveASeatAction.userInfoKeys.reservationId.getString] as? String,
success {
reservationId = id
showAlert = true
}
}
.alert("Payment Successful", isPresented: $showAlert, presenting: reservationId) { id in
Button("OK", role: .cancel) {}
} message: { id in
Text("Reservation \(id) has been successfully paid.")
}
}
}
Explanation:
Listening for Notifications
: The.onReceive
modifier listens for theReserveASeatAction
notification. When it detects a successful payment(success == true)
, it updatesreservationId
and setsshowAlert
totrue
.- Alert Display: The
.alert
modifier displays a message with the reservation ID, confirming the successful payment.
Putting It All Together in a Root View
To test this setup, we can create a root view that shows ExploreView
and a button to navigate to BookingView
.
import SwiftUI
struct ContentView: View {
@State private var showBookingView = false
var body: some View {
NavigationStack {
VStack {
ExploreView()
Button("Book a Seat") {
showBookingView = true
}
.buttonStyle(.borderedProminent)
}
.navigationTitle("Main View")
.navigationDestination(isPresented: $showBookingView) {
BookingView()
}
}
}
}
In ContentView:
- We provide a button that navigates to
BookingView
. - The
ExploreView
listens for the notification and displays an alert with the reservation ID upon successful payment.
Explanation and Flow
- Booking a Seat: The user clicks “Book a Seat” on
ContentView
, which navigates toBookingView
. - Processing Payment: In
BookingView
, the user initiates the payment by pressing “Pay Now.” The mock payment processing displays a loading indicator. - Sending Notification: When the payment completes,
BookingView
posts a notification with thereservationId
. - Receiving Notification:
ExploreView
observes the notification. When a successful payment notification is received, it triggers an alert showing thereservationId
.