WatchKit 2.0 Send Message From Phone To Watch - ios

I'm wanting to be able to update my Apple Watch views if a user has both the Apple Watch app open and the iPhone app open as well. I know there is a WatchKit 1 question asked here, but I want to know if I could do this using WatchConnectivity.
Within my iOS app, I send a message:
if WCSession.isSupported() {
// Set the session to default session singleton
let session = WCSession.defaultSession()
// Fire the message to watch
NSLog("send message")
session.sendMessage(["action": "messageAction"], replyHandler: nil, errorHandler: { (error) -> Void in
// Display alert
NSLog(error.description)
})
}
But I keep getting the error:
Error Domain=WCErrorDomain Code=7007 "WatchConnectivity session on paired device is not reachable." UserInfo={NSLocalizedDescription=WatchConnectivity session on paired device is not reachable.}
To send messages from the iPhone to Apple Watch, is WatchConnectivity sendMessage the correct method to use?

It's only the "correct" method if you are looking to interactively communicate with a reachable device.
But you haven't shown where you set your session delegate or activated the session:
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
You may also want to add some checks to ensure that a watch is paired (session.paired), that the watch app is installed (session.watchAppInstalled), and that the watch is actually reachable (session.reachable) before trying to interactively communicate with it, to make your app more robust.
See this guide for more details.
You could also choose to fall back on a non-interactive (background) method to queue messages for your app until it is open and can receive them.

Related

How to make WCSession reachable when the watchOS app is running in background with HKWorkoutSession

My watchOS app uses workout API in order to stay running while the app goes to background. The issue is that WCSession becomes unreachable when the app is in background. However, I'm able to run my code and on some condition, it needs to send a message to the iPhone counterpart app.
The specifics of the app require that user doesn't have to interact with it - if there is a timeout, the watch app should send the message to the phone automatically.
Is this possible to achieve? Thanks.
I believe the handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) method is what you're looking for. Documentation Link
Without seeing your code I can't be sure what your current progress is, but I have used this method to update watchOS Complications in the background as the user's location changes.
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
if WKExtension.shared().applicationState == .background {
// Do your background work here.
if let watchComplication = task as? WKWatchConnectivityRefreshBackgroundTask {
pendingConnectivityTasks.append(watchComplication)
}
}
task.setTaskCompletedWithSnapshot(true)
}
completePendingConnectivityTasksIfNeeded()
}
As a side note I will add if your app is not an actual workout app, it will get rejected during App Review for using a HealthKit workout session.

Why can my Apple Watch OS app receive message from my iOS app only when it is active?

I am trying to build a jogging app that communicates with the Apple Watch app. When I press the "Start" button, the iOS app should signal my Watch app to start keeping track of my workout. (e.g. Showing time elapsed, measure heart rate, and etc) To make that possible, the iOS and WatchOS app should communicate. The problem with my app is that my WatchOS app can only receive a signal from my iOS app when it is on. (e.g. Watch screen is on)
This is a code from my iOS app:
#objc func startAction() {
if WCSession.isSupported() {
print("WC session is supported...")
let session = WCSession.default
session.delegate = self
session.activate()
session.sendMessage(["testWorkout":true], replyHandler: nil) { error in
print("ERROR: \(error.localizedDescription)")
}
}
}
And this is code from the other end (From the watch's end):
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
WKInterfaceDevice.current().play(.start)
print("message test workout")
}
I get a play sound and the printout message: "message test workout" when Apple Watch is on, but
when Apple Watch's screen is off, the WatchOS app receives no signal. What code can I write from iOS app's end (Or anything else I can do from WatchOS app's end) to wake up the WatchOS app?
You should not call sendMessage straight after activate - The session may not (and probably won't) be active.
You need to wait until you get the activationDidCompleteWith delegate callback and then you can attempt communication.
Before attempting to send data you should check that the session state is .active and reactivate the session if it is no longer active.

WCSession to check if companion app can receive message

Im using WCSession to communicate between apple Watch and iOS App.
on both sides im initiating the session with:
init() {
self.session = WCSession.default
self.session.delegate = self
self.session.activate()
}
also implemented the method to activate the session when he deactivated, when session deactivated i call session.activate()
in a case where the companion app is terminated, i want the watch app to get immediate response, instead i get timeout after a minute or so.
tried to use the WCSession.isreachable but it always returns true.
is there a way for one of the session sides to check if the other side can receive the message?
thanks!

LocalNotification with only sound and haptic on watchOS2

How do I trigger a UILocalNotification from the iPhone which would have no alert but only play a sound / haptic feedback on the Apple Watch?
Further background:
I am making a watchOS2 timer app. I use a UIlocalNotification triggered by the iPhone to tell the user that end of timer is reached (to handle the scenario where the watch is not active).
The problem is that Apple uses its own logic to determine if a notification appears on the watch or the phone. If I trigger a notification with no alert but only sound, this notification always plays on phone, never on the watch.
I'd prefer that notification sound/haptic to play on the watch. How can I achieve this?
The downside of what you're asking:
A sound-only notification on the watch would be confusing.
Without a message associated with the sound, the user wouldn't see any reason for the notification, when they glanced at their watch.
The downside of how to currently do what you're asking:
The WKInterfaceDevice documentation points out Apple's intention for playing haptic feedback:
You can also use this object to play haptic feedback when your app is active.
What follows is a misuse of the API to accomplish something it wasn't intended to do. It's fragile, potentially annoying, and may send users in search of a different timer app.
Changes for iOS 10 prevent this from working, unless your complication is on the active watch face.
How you could currently do what you're asking in watchOS 2:
To provide haptic feedback while your app is not active, you'd need a way for the watch extension to immediately wake up in the background, to provide the feedback:
WKInterfaceDevice.currentDevice().playHaptic(.Notification)
To do this, you could misuse the WCSession transferCurrentComplicationUserInfo method. Its proper use is to immediately transfer complication data from the phone to the watch (that a watch face complication might be updated). As a part of that process, it wakes the watch extension in the background to receive its info.
In your phone's timerDidFire method:
After checking that you have a valid Watch Connectivity session with the watch, use transferCurrentComplicationUserInfo to immediately send a dictionary to the watch extension.
guard let session = session where session.activationState == .Activated && session.paired && session.watchAppInstalled else { // iOS 9.3
return
}
let hapticData = ["hapticType": 0] // fragile WKHapticType.Notification.rawValue
// Every time you misuse an API, an Apple engineer dies
session.transferCurrentComplicationUserInfo(hapticData)
As shown, the dictionary could contain a key/value pair specifying the type of haptic feedback, or simply hold a key indicating that the watch should play a hardcoded notification.
Using an internal raw value is fragile, since it may change. If you do need to specify a specific haptic type from the phone, you should setup an enum instead of using a magic number.
In the watch extension's session delegate:
watchOS will have woken your extension in preparation to receive the complication user info. Here, you'd play the haptic feedback, instead of updating a complication, as would be expected.
func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {
if let hapticTypeValue = userInfo["hapticType"] as? Int, hapticType = WKHapticType(rawValue: hapticTypeValue) {
WKInterfaceDevice.currentDevice().playHaptic(hapticType)
}
}
The proper solution:
Request that Apple provide a way to schedule local notifications on the watch. Apple's timer app does this already.
You may also wait to see what is announced at WWDC 2016, to decide if any new functionality available in watchOS 3 would help to create a proper standalone watch timer app.

Check if user has AppleWatch connected without prompting the watch

We are using Google Analytics, and want to know how many of our users are in possession of an AppleWatch. I have searched Stack for answers, and the recurring answer is to use this:
if WCSession.isSupported() { // check if the device support to handle an Apple Watch
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession() // activate the session
if session.paired { // Check if the iPhone is paired with the Apple Watch
// Do stuff
}
}
The problem with this is that it prompts the user to 'accept' the app on the AppleWatch. Granted, the if-statement is true wether or not the user accepts, but I don't want the user to get their hopes up, thinking the app supports AppleWatch. I simply want to know if the user has an AppleWatch, I don't want to use it. Yet.
Is there a property on the iOS-device that can be accessed to show if the user has or ever had an AppleWatch connected, without prompting the user through the Watch?
Probably you can use (Push)Notifications, since there is no need to develop a native applewatch app to receive notifications on the watch. For example i receive "whats app messages" on my watch, but it does not have any native app on the watch either.
In your watchkit extension ExtensionDelegate.m you can handle provided answers to the push message separately. Link to Apple
This would be a(nother) approach, where you have to be creative!

Resources