i-swift

Master the Art of Swift Programming

Using NotificationCenter in SwiftUI to send data between different parts of an app

In 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:

  1. BookingView – Simulates a payment process and sends a success notification.
  2. 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 }
}
Swift

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 as success (a boolean indicating if the payment was successful) and reservationId (the unique ID for the reservation).
  • getString: This property returns the string value for each key, so we can use these keys easily in userInfo.

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
            ]
        )
    }
}
Swift

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 name ReserveASeatAction.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.")
        }
    }
}
Swift

Explanation:

  • Listening for Notifications: The .onReceive modifier listens for the ReserveASeatAction notification. When it detects a successful payment (success == true), it updates reservationId and sets showAlert to true.
  • 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()
            }
        }
    }
}
Swift

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

  1. Booking a Seat: The user clicks “Book a Seat” on ContentView, which navigates to BookingView.
  2. Processing Payment: In BookingView, the user initiates the payment by pressing “Pay Now.” The mock payment processing displays a loading indicator.
  3. Sending Notification: When the payment completes, BookingView posts a notification with the reservationId.
  4. Receiving Notification: ExploreView observes the notification. When a successful payment notification is received, it triggers an alert showing the reservationId.

Leave a Reply

Your email address will not be published. Required fields are marked *

Quick contact info

© 2024 i-Swift. All rights reserved
Your go-to source for Swift tutorials, tips, and insights. Empowering developers with expert content since 2024.