I'm using WatchConnectivity and the WCSessionDelegate to successfully receive user info from my iOS app to my watchOS extension. When new data is received, I want to update my SwiftUI view with the latest information. I have an @ObservedObject property in the HostingController which is set to the latest data when it arrives. However, this does not trigger a UI update. How can I fix this? I've posted the code below. Thank you!
import WatchKit
import SwiftUI
import WatchConnectivity
import Combine
class UserData: ObservableObject {
@Published var account: Account = Account.getCurrentAccount()
}
class HostingController: WKHostingController<ContentView>, WCSessionDelegate {
@ObservedObject private var userData: UserData = UserData()
override init() {
super.init()
if WCSession.isSupported() {
print("WCSession supported")
let session = WCSession.default
session.delegate = self
session.activate()
}
}
override var body: ContentView {
return ContentView(userData: userData)
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
}
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
guard let accountJSON = userInfo["main-account"] as? String else { return }
let userDefaults = UserDefaults()
userDefaults.set(accountJSON, forKey: "main-account")
self.userData.account = Account.getCurrentAccount()
print("\n\nReceived this: \(accountJSON) \n\n")
}
}
There are a couple of things you can do here.
First of all, you don't show what ContentView looks like. That's the view that needs to be updated, so that's what needs to use the @ObservedObject property wrapper. Note that WKHostingController is not a View, so SwiftUI isn't going to do anything automatically based on changes to its content. The ContentView, though, is a View, so if that contains an @ObservedObject property then everything ought to work.
The second option may be the better, though. Look at the interface definition for WKHostingController:
open class WKHostingController<Body> : WKInterfaceController where Body : View {
/// The root `View` of the view hierarchy to display.
open var body: Body { get }
/// Invalidates the current `body` and triggers a body update during the
/// next update cycle.
public func setNeedsBodyUpdate()
/// Update `body` immediately, if updates are pending.
public func updateBodyIfNeeded()
@objc override dynamic public init()
}
There's a method there called setNeedsBodyUpdate() which ought to do exactly what you need. Simply add a call to self.setNeedsBodyUpdate() inside your session(_:didReceiveUserInfo:) implementation, for instance at line 39 of your example, and you should see everything update properly.