[SwiftUI] Real-time Data Subscription and Updates with Combine

SwiftUI and Combine make asynchronous data processing in iOS app development simple and efficient. In particular, you can automatically refresh views whenever data is updated in real-time by leveraging @Published and Combine's subscription features. In this post, using the ProfileViewModel example, we will introduce how to update data in real-time and manage it efficiently in SwiftUI using Combine.

Code Example: ProfileViewModel


class ProfileViewModel: ObservableObject {
    @Published var currentUser: User?
    private var cancellables = Set<AnyCancellable>()

    init() {
        setupSubscribers()
    }

    private func setupSubscribers() {
        UserService.shared.$currentUser.sink { [weak self] user in
            self?.currentUser = user
        }.store(in: &cancellables)
    }
}

This code defines ProfileViewModel, which updates user profile data in real-time and reflects it in the SwiftUI view. You can easily set up state synchronization with SwiftUI by using Combine-based data subscription and the @Published property wrapper.

Code Analysis

1. class ProfileViewModel: ObservableObject

ProfileViewModel conforms to the ObservableObject protocol. This allows the ViewModel to provide data to SwiftUI views and enables the view to automatically update when the data changes.

2. @Published var currentUser: User?

The @Published property wrapper ensures that whenever currentUser is updated, SwiftUI detects the change and re-renders the view. This allows you to manage the data flow reactively, ensuring that changes to user information are immediately reflected in the view.

3. private var cancellables = Set<AnyCancellable>()

This is a collection that stores AnyCancellable objects, which represent subscriptions that can be cancelled. It is important in Swift to manage Cancellables to prevent memory leaks and cancel subscriptions when they are no longer needed.

4. init() and setupSubscribers()

The init method is called when the ProfileViewModel object is created, and it initializes the Combine subscription setup via the setupSubscribers() method.

5. UserService.shared.$currentUser.sink { [weak self] user in ... }

This is the core of Combine. It subscribes to currentUser, which is declared as a @Published property in UserService, and executes the closure whenever the data changes. [weak self] is used to prevent retain cycles, capturing self weakly to ensure proper memory management.

6. self?.currentUser = user

The user object passed from the data stream is assigned to the ViewModel's currentUser. As a result, the SwiftUI view detects the changed data and is re-rendered accordingly.

7. print("DEBUG: User in view model from combine is \(user)")

This code prints the data received via Combine for debugging purposes. It is useful for verifying real-time data flow.

8. .store(in: &cancellables)

After completing the subscription, it is stored in cancellables to enable memory leak prevention and subscription management. This method ensures that the Combine subscription is automatically released from memory when it is no longer required.


Why is this code necessary?

  1. Real-time data reflection: It is highly useful when user information changes within the app and needs to be reflected in real-time. For example, when a user logs in or logs out, this information can be updated automatically in the ViewModel.
  2. Memory management: To manage memory efficiently when a subscription ends, you must be able to cancel it. Using Combine's AnyCancellable allows you to release unnecessary subscriptions, preventing memory leaks.
  3. Reactive app design: One of the strengths of SwiftUI is the reactive flow between data and views. Using Combine allows the view to update automatically whenever data changes, providing users with a natural, real-time UI experience.

Conclusion

Using Combine makes it easy to subscribe to data flow and update views whenever the state changes in real-time. This mechanism, combined with SwiftUI, can be a powerful tool in modern iOS app development. In particular, the subscription pattern between @Published and Combine is essential for managing data flow efficiently and handling memory management.

References

AD