Sharing data between iOS and watchOS application (and widget) - WatchConnectivity

802 views Asked by At

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

0

There are 0 answers