1

I want to know a better way to send (with iPhone) message to Watch (in background). Currently i use:

session.update(applicationContext: package, completion: nil)

for sending messages to Watch and

 func didReceivedApplicationContext(applicationContext:[String : Any])

to receive messages in Watch. The problem is that as is said in documentation "The system will transfer content at opportune times" and i can't control those "oportune times".

At this time, i check in iPhone class the state of Watch. If the Watch is on Background, i send data with updateApplicationcontext (which is not verry good), else, if Watch is on foreground, i send data with sendMessage

Code:

 if session.isWatchReachable()
    {
        session.send(message: package, completion: nil)
    }
    else
    {
        session.update(applicationContext: package, completion: nil)
    }

So is a better way to transfer data in background?

2 Answers2

0

Well you have to choose between background transfers and sending data immediately. Using the WatchConnectivity framework there is no way to schedule data transfer to happen at an exact moment in time.

You have to choose between using a method that can send data even while your app is in the background or sending data immediately from the foreground.

If you need a guarantee that the data will be transferred by the time your app wakes up from the background and for your use case updateApplicationContext proved to be insufficient during testing, you can use a workaround.

The workaround is to use updateApplicationContext to transfer data in the background and in your Watch app's ExtensionDelegate class, use the applicationDidBecomeActive() method to check whether the data transfer has happened in the background and if it did not happen, use the sendMessage function to request the data immediately from the other app and send back the requested data as a response.

To implement this, your function should look something like this on your Watch app:

func applicationDidBecomeActive(){
    if !haveReceivedData {  //Bool value that should be updated to true once data is received from the phone
        session.sendMessage(["Requesting data":True], replyHandler: {
            //parse the data, update the UI
        }, errorHandler: { error in
                    print("Error requesting data",error)
                })
    }
}

And on your iPhone app you do the following:

func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
        if let isRequestingData = message["Requesting data"] as? Bool, isRequestingData == true {
            replyHandler(["Requested data":data])
        }
    }
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • How could i request the data with sendMessage if i send data with updateApplicationContext? –  Aug 10 '17 at 13:46
  • By the way, at this time, i check in iPhone class the state of Watch. If the Watch is on Background, i send data with updateApplicationcontext (which is not verry good), else, if Watch is on foreground, i send data with sendMessage. –  Aug 10 '17 at 13:48
  • Would it make any difference if i move this code (explained above) in ExtensionDelegate class applicationDidBecomeActive? –  Aug 10 '17 at 13:49
  • For your first comment: you send a new request from the Watch using `sendMessage` in `applicationDidBecomeActive` and send the data that you have already sent before, but haven't received from the phone using `updateApplicationContext` in the response of the `sendMessage` function. – Dávid Pásztor Aug 10 '17 at 13:56
  • For your last comment: if you moved the whole function to `applicationDidBecomeActive`, you wouldn't need to use `updateApplicationContext` at all, since in this case you would only be sending data when the `Watch` app would be active. However, keep in mind, that even with `sendMessage` the data sending might take a couple of seconds, so the user might not see the updated UI right away. – Dávid Pásztor Aug 10 '17 at 13:58
  • "and send the data that you have already sent before, but haven't received from the phone using updateApplicationContext" How could i send data from Watch to iPhone if i didn't received data from iPhone? –  Aug 10 '17 at 14:05
  • You are not sending data from the Watch to the phone. You are sending a "request" using `sendMessage` by sending an otherwise meaningless dictionary as the `userInfo` from the Watch. In the `func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void)` function you send the data back to the watch using the `replyHandler`. – Dávid Pásztor Aug 10 '17 at 14:08
  • Check my updated answer, I have added a simplified example. – Dávid Pásztor Aug 10 '17 at 14:14
  • So you are telling me, in the case that the message wasn't received on Watch, to send a request from Watch to iPhone to notify that the message wasn't sent, and to send again data from iPhone to Watch? But how can i check if the message wasn't sent? –  Aug 10 '17 at 14:19
  • Yes, this is what you should do. You can set up a property observer on the variables that need to be updated using the data from the Phone and unless their value changes request the data. – Dávid Pásztor Aug 10 '17 at 14:20
  • And is necessary to make this request in applicationDidBecomeActive? If the watch is on background, it wouldn't be active so the request would not be sent. –  Aug 10 '17 at 14:23
  • If you want to receive the data as soon as your app becomes active, yes. – Dávid Pásztor Aug 10 '17 at 14:23
  • Oh, but is possible that my watch app to be a lot in background, i can't wait to become active. –  Aug 10 '17 at 14:26
  • Then as I have told you in my answer, there is no way to guarantee the time your data will be sent. What would you need the data for anyways when your Watch app is in the background? – Dávid Pásztor Aug 10 '17 at 14:28
  • I need to get the vibration in background when i sent request from iPhone (is a app for detecting obstacles, and when it detects obstacle, the watch should vibrate to notify the driver). The driver would have mostly all the time watch app in background. –  Aug 10 '17 at 14:31
  • Sadly for this behaviour you cannot use the `WatchConnectivity` framework at the moment. You can only send instant messages in the foreground. You either have to detect the obstacles on the `Watch` itself or find a completely different approach that doesn't use the `WatchConnectivity` framework at all. – Dávid Pásztor Aug 10 '17 at 14:41
  • However, i'll try to make a combination of updateApplicationcontext and transferCurrentComplicationUserInfo (at the last one i have a maximum number of 50 request :|). –  Aug 11 '17 at 05:40
  • Why would you combine those two? Both only work in the background and you have no control of when they send the info. Moreover, `transferCurrentComplicationUserInfo` should only be used to transfer `Complication` data and it can only be called a limited number of times... From what I understood, you are not even using a `Complication`, so why would you call a function to update its data? – Dávid Pásztor Aug 11 '17 at 08:51
  • When the message is not sent with ApplicationContext, i will try to send it with transferCurrentComplicationUserInfo (which is for sure that is transmited, but has maximum 50 request per day). Combining those 2, i'll get a higher number than 50. But ofcourse, after that i can't guarantee that the message is received. That's the problem that i'm encountering. I can use transferCurrentComplicationUserInfo even if i'm not using a complication but i have to enable it. –  Aug 11 '17 at 09:23
  • This sounds like a misuse of Apple Watch resources. I would rather consider finding a not so hacky alternative or just drop the idea of making this a Watch app. Apple is really restrictive of what you can and what you cannot do on the Watch and sometimes it's worth dropping an idea if it is not possible with the current WatchKit resources. – Dávid Pásztor Aug 11 '17 at 09:28
  • My mentor has implement this idea. I can't tell him to drop this app. –  Aug 11 '17 at 09:31
  • The answer is incorrect, it suggests to check if data is received on watch while it's against the business logic (data is pushed from iPhone to iWatch). – Alexander Volkov Mar 26 '23 at 14:52
-1

It's not possible to receive data on iWatch in background. You can only receive data in foreground. updateApplicationContext() method is designed especially for sending data in background (from iPhone to iWatch). When the watch app will be activated (opened) the context data will be available for usage - no need to worry.

15:43 - https://developer.apple.com/videos/play/wwdc2021/10003/

So, what you did in the answer is a correct solution.

Alexander Volkov
  • 7,904
  • 1
  • 47
  • 44