I am trying to send data from iOS application to watchOS app and widget (using WatchConnectivity). Former receives data just fine, but I wasn't able to figure out why data does not make it to the widget on watchOS.
Here's some code:
My iOS app that sends data:
struct ContentView: View {
var shared = Shared()
var body: some View {
VStack {
Button {
self.sendMessage(username: "David")
} label: { HStack {Text("David").font(.title).padding()} }
Button {
self.sendMessage(username: "John")
} label: { HStack {Text("John").font(.title).padding()} }
}
.padding()
}
private func sendMessage(username: String) {
self.shared.session.transferCurrentComplicationUserInfo(["username": username])
}
}
Here comes watchOS app:
struct ContentView: View {
@ObservedObject var shared = Shared()
var body: some View {
VStack {
HStack {
if let username = self.shared.username {
Text("Hello \(username)")
} else {
Text("Check iPhone app.")
}
}
}
}
}
On watchOS extension the most important part is getTimeline
function so I'll omit the rest for now:
struct Provider: IntentTimelineProvider {
var shared = Shared()
...
func getTimeline(for configuration: ConfigurationIntent, in context: Context,
completion: @escaping (Timeline<Entry>) -> Void) {
shared.fetchData { user in
let entries = [
SimpleEntry(user: user)
]
let timeline = Timeline(entries: entries, policy: .never)
completion(timeline)
}
}
...
}
Finally, there is the Shared
class that is shared between all applications and extension (File inspector -> Target Membership (checked all three)
.
import WatchConnectivity
import WidgetKit
final class Shared: NSObject, ObservableObject {
@Published var username: String?
var session: WCSession
init(session: WCSession = .default) {
self.session = session
super.init()
self.session.delegate = self
session.activate()
}
func fetchData(completion: @escaping (String) -> Void) {
completion(self.username ?? "")
}
}
extension Shared: WCSessionDelegate {
#if os(iOS)
func sessionDidBecomeInactive(_ session: WCSession) {}
func sessionDidDeactivate(_ session: WCSession) {}
#else
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String: Any] = [:]) {
DispatchQueue.main.async {
self.username = userInfo["username"] as? String
WidgetCenter.shared.reloadAllTimelines()
}
}
#endif
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?) {}
}
I suspect this might be related to widget being kept in the background, although I know nothing about application lifecycle on iOS whatsoever. I came across WKWatchConnectivityRefreshBackgroundTask
but couldn't make it work. If it helps, I placed this example project on my Github. I'd really appreciate any help on this one. Cheers