How to quickly load data on Apple Watch? UserDefaults doesn't work since watchOS 2, so we can only use WCSessionDelegate, right?
Now, on Watch App start I call wcSession?.sendMessage(someThing, replyHandler: someFunc, errorHandler: otherFunc), then on iPhone app I send back some data in
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void)
And finally receive it on watch app in func session(_ session: WCSession, didReceiveMessage message: [String : Any]), but that takes like 3 seconds.
What would be better way to get data on start up?
You could try using the reply Handler function instead of initiating a new message. It should work faster.
Call this from watch:
func sendRequest() {
if session.activationState == .activated && session.isReachable {
session.sendMessage(["Watch Message" : "Request"], replyHandler: { (reply) in
// Handle reply here
}, errorHandler: { (error) in
print("***** Error Did Occur: \(error) *****")
})
}
}
Handle and Respond on Phone:
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
if let messageFromWatch = message["Watch Message"] {
let messageData = messageFromWatch as! String
// Message From Watch to Activate Watch Connectivity Session
if messageData == "Request" {
replyHandler(["Response" : data])
}
}
}
Related
I'm stuck with the following problem: I'm able to start WCSessions on both iPhone and Watch (simulator and real devices) and can use the sendMessage method just fine. I'm aware that updateApplicationContext requires an updated dict to be sent, so I'm adding an uuid for debugging purposes:
context = ["user":id,"messageId": UUID().uuidString]
There's no error thrown when the method is called but on the watch side didReceiveApplicationContext never gets called and the receivedApplicationContext dict stays empty all the time. After reading a lot of similar code examples and the docs I can't see where I'm wrong.
I'm building with XCode 12.5 and iOS 14.5 and watchOS 7.4
Here's the iOS code dealing with the WCSession, context is set in another method and is successfully transferred with sendMessage but not with updateApplicationContext:
import WatchConnectivity
public class CDVSettings : CDVPlugin, WCSessionDelegate {
var wcSession : WCSession! = nil
var didSendMessage:Bool = false
var context: [String : Any] = [:] {
didSet {
debugPrint("context didSet:", self.context)
debugPrint("WCSession.isPaired: \(wcSession.isPaired), WCSession.isWatchAppInstalled: \(wcSession.isWatchAppInstalled)")
if wcSession.activationState == WCSessionActivationState.activated {
do {
debugPrint("updateApplicationContext is called")
self.didSendMessage=true
try wcSession.updateApplicationContext(self.context)
}
catch let error as NSError {
debugPrint(error.localizedDescription)
}
catch {}
wcSession.sendMessage(self.context, replyHandler: { reply in
print("Got reply: \(reply)")
}, errorHandler: { error in
print("error: \(error)")
})
} else {
print("activationState is not activated")
wcSession.activate()
}
}
}
public func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
print("activationDidCompleteWith activationState:\(activationState) error:\(String(describing: error))")
}
public func sessionDidBecomeInactive(_ session: WCSession) {
}
public func sessionDidDeactivate(_ session: WCSession) {
}
#objc(pluginInitialize)
public override func pluginInitialize() {
wcSession = WCSession.default
wcSession.delegate = self
wcSession.activate()
}
[...]
}
And here's the watch part:
import WatchConnectivity
class ConnectivityRequestHandler: NSObject, ObservableObject, WCSessionDelegate {
var session = WCSession.default
override init() {
super.init()
session.delegate = self
session.activate()
debugPrint("ConnectivityRequestHandler started with session", session)
}
// MARK: WCSession Methods
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
debugPrint(error)
debugPrint("session is reachable:",session.isReachable)
debugPrint("last received application context:",session.receivedApplicationContext)
}
func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: #escaping ([String: Any]) -> Void) {
debugPrint("didReceiveMessage: \(message)")
replyHandler(["message received": Date()])
}
func session(_ session: WCSession, didReceiveMessageData messageData: Data, replyHandler: #escaping (Data) -> Void) {
debugPrint("didReceiveMessageData: \(messageData)")
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
debugPrint("did receive application context")
debugPrint(applicationContext)
if let id = applicationContext["user"] as? [String]{
debugPrint("\(id)")
UserDefaults.standard.set(id, forKey: "user")
}
}
}
The corresponding logs on iOS
"context didSet:" ["user": "0e4a28b5-f8a0-40a5-942d-5f13b610a93a",
"messageId": "8A78246C-91E6-48DC-B55D-4F4EBC761211"]
"WCSession.isPaired: true, WCSession.isWatchAppInstalled: true"
"updateApplicationContext is called" Got reply: ["message received":
2021-09-03 08:31:54 +0000]
and on watchOS
"ConnectivityRequestHandler started with session" <WCSession:
0x600003be0b40, hasDelegate: YES, activationState: 0> nil "session is
reachable:" true "last received application context:" [:]
"didReceiveMessage: ["user": 0e4a28b5-f8a0-40a5-942d-5f13b610a93a,
"messageId": 8A78246C-91E6-48DC-B55D-4F4EBC761211]"
What am I doing wrong? Thank you so much for your help.
For those stumbling over my question while researching, it's no real solution, but an observation that I (now) share with others (see comments above): With real devices it works as expected, but you may need to reboot them in the process. In the simulators it seems that different methods may work while others won't and that even differs from developer to developer.
In Xcode 14 this should now also work for Simulator. At least for me it seems to work without problems
Test with iphone11(14.6) and watch series3(7.5)
In my project, I use WatchConnectivity between phone and watch to communication,
When watch send message with below code, every time run is OK.
wcSession.sendMessage(message as [String : Any], replyHandler: nil)
But when watch send message need reply, it seems error.
wcSession.sendMessage(message) { (reply) in
} errorHandler: { (error) in
}
In Phone with below code to deal with request
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
}
I debug the code, didReceiveMessage with reply data can be called, but watch can not receive the reply. So what’s my problem, Before the mobile and watch update, everything is good, every update will cause problems, how to ensure the stability of the app?Thank you very much.
Working on a personal project and trying to receive content from an Apple Watch. The data being sent is fine but I get no response from either the phone or the watch.
Inside AppDelegate I have a session started inside didFinishLaunchingWithOptions and it conforms to WCSessionDelegate.
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
var replyValues = [String : Any]()
switch message["command"] as! String {
case "logAttempt":
print("Log attempt")
replyValues["response"] = "true"
case "getData":
print("Get course data")
replyValues["response"] = "true"
default:
break
}
replyHandler(replyValues)
Inside the WatchKit class it also conforms to WCSessionDelegate and has the following to send message to the phone.
session.sendMessage(data, replyHandler: { replyHandler in
print(replyHandler)
if let response = replyHandler["response"] as? String {
print("app: Response from app to watch: \(response)")
} else {
print("error: Somethings wrong!: \(replyHandler["response"])")
}
}) { error in
print("Error: \(error.localizedDescription)")
}
ExtensionDelegate.swift file also conforms to WCSessionDelegate and starts a session inside applicationDidFinishLaunching. Struggling to get this working.
Thanks!
Edit: Setting up watch inside appDel:
fileprivate func setupWatch() {
if WCSession.isSupported() {
session = WCSession.default
session!.delegate = self
session!.activate()
}
}
I'm working on a watch app. I have to send data from the mobile to watch.
This is working properly.
The main problem occurs when I'm sending message from watch to mobile app because I want that the communication to be initiated by watch app. I have implemented the method to send data from watch to mobile app but always getting this
"WCErrorDomain Code=7014 "Payload could not be delivered." error.
I'm using xcode 7.3 and watchOS version is 2.0.
One more thing: is it possible that the sendMessage method opens the mobile app in background using watch connectivity framework?? Or if anyone knows any other way to open the mobile app in background then please suggest me. Thanks in advance.
session.sendMessage(message, replyHandler: { reply in
}, errorHandler: { error in
print("error: \(error)")
})
EDIT
In AppDelegate and ExtensionDelegate:
override init() {
super.init()
setupWatchConnectivity()
}
private func setupWatchConnectivity() {
if WCSession.isSupported() {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
}
In ExtensionDelegate:
func sendMessage(){
let session = WCSession.defaultSession()
let applicationData:[String:AnyObject] = ["text":"test", "badgeValue": 100 ]
WatchSessionManager.sharedManager.sendMessage(applicationData, replyHandler: {replyMessage in
print("reply received from iphone")
}, errorHandler: {(error ) -> Void in
// catch any errors here
print("no reply message from phone")
})
}
In AppDelegate:
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
let text = message["text"] as! String
let badgeValue = message["badgeValue"] as! Int
dispatch_async(dispatch_get_main_queue()) { () -> Void in
print("iphone received message from watch App")
self.sendNotification(text, badgeValue: badgeValue)
let applicationDict = ["wake": "nowAwake"]
replyHandler(applicationDict as [String : String])
}
}
In watchSessionManager :
func sendMessage(message: [String : AnyObject], replyHandler: (([String : AnyObject]) -> Void)?, errorHandler: ((NSError) -> Void)? = nil){
session.sendMessage(message, replyHandler: { reply in
}, errorHandler: { error in
print("error: \(error)")
})
print("mesage %#",message)
}
I use the WCSession.defaultSession().sendMessage function to send a message from the watch to the iphone, with non nil replyHandler closure
on the iphone,
func session(session: WCSession, didReceiveMessage message: [String : AnyObject])
is called and not the callback that should be called:
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void)
and thus i cannot call replyHandler :-(
any idea why?
thanks a lot!
Edit:
code used to make the call:
WCSession.defaultSession().sendMessage(applicationDict, replyHandler: { (reply : [String : AnyObject]) -> Void in
...
},
errorHandler: { (er : NSError ) -> Void in
// Handle error
print(er.description)
});