Send Messages Between iOS and Watchos with Watchconnectivity in Watchos2

Send messages between iOS and WatchOS with WatchConnectivity in watchOS2

WatchConnectivity

First the two classes that are supposed to communicate with each other (iOS and watchOS) need to conform the <WCSessionDelegate> and #import the WatchConnectivity framework

Before you can send data you need to check if your device is able to send data

if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
NSLog(@"WCSession is supported");
}

Then if you wish to use "interactive messaging" (sendMessage APIs) you will need to see if the other device is reachable first:

if ([[WCSession defaultSession] isReachable]) {
//Here is where you will send you data
}

The "background operations" APIs do not require the counterpart device to be reachable at the point in time you call the WCSession API.

You have several options when it comes to transferring data between your apps, in the Apple Documentation they are described like this:

  • Use the updateApplicationContext:error: method to communicate only the most recent state information to the counterpart. When the counterpart wakes, it can use this information to update its own state and remain in sync. Sending a new dictionary with this method overwrites the previous dictionary.

  • Use the sendMessage:replyHandler:errorHandler: or sendMessageData:replyHandler:errorHandler: method to transfer data immediately to the counterpart. These methods are intended for immediate communication when your iOS app and WatchKit extension are both active.

  • Use the transferUserInfo: method to transfer a dictionary of data in the background. The dictionaries you send are queued for delivery to the counterpart and transfers continue when the current app is suspended or terminated.

  • Use the transferFile:metadata: method to transfer files in the background. Use this method in cases where you want to send more than a simple dictionary of values. For example, use this method to send images or file-based documents.

I will give you an example how to send/receive data with Application Context

Send data:

WCSession *session = [WCSession defaultSession];
NSError *error;

[session updateApplicationContext:@{@"firstItem": @"item1", @"secondItem":[NSNumber numberWithInt:2]} error:&error];

Receive data:

- (void) session:(nonnull WCSession *)session didReceiveApplicationContext:(nonnull NSDictionary<NSString *,id> *)applicationContext {

NSLog(@"%@", applicationContext);


NSString *item1 = [applicationContext objectForKey:@"firstItem"];
int item2 = [[applicationContext objectForKey:@"secondItem"] intValue];
}

For more information about WatchConnectivity I really recommend watching the WWDC2015 session video and reading the Apple Documentation on WatchConnectivity

Communicate from Watch to killed iOS app using WatchConnectivity

You have to make sure to set up the WCSession in a part of your iOS app that gets run when the iOS app is running in the background (UIApplicationDelegate application:didFinishLaunchingWithOptions: would be one of these spots, while any UIViewController's viewDidLoad would not be one of those)

How can I share information between my iOS and Watch apps using WatchConnectivity?

At the time of writing this answer (watchOS3 is the current stable release and watchOS4 is in beta stage), the only option for direct communication between an iOS app and its WatchKit extension is the WatchConnectivity framework. (I said direct, because this Q&A is not concerned with using cloud technologies such as CloudKit to upload files to the internet from one device and download them on the other one.)

First, let's discuss which function of WCSession should be used for what purpose. For the code examples, please scroll down.

A quick, one-liner about each function and when to use them before diving deep into details:

  • updateApplicationContext: synchronise states between the apps, send data to be displayed on the UI (only use it to send small pieces of data)
  • transferUserInfo: send a dictionary of data in the background
  • transferFile: send a file in the background
  • sendMessage: send an instant message between at least the watch app is running in foreground

Detailed description

updateApplicationContext(_:) should be used if you want to synchronise your apps (such as keep the UI updated or send state information, like user logged in, etc.). You can send a dictionary of data. Subsequent calls to this function replace the previously sent dictionary, so the counterpart app only receives the last item sent using updateApplicationContext. The system tries to call this function at an appropriate time for it to receive data by the time it is needed and meanwhile minimising power usage. For this reason, the function can be called when neither app is running in the foreground, but WCSession needs to be activated for the transfer to succeed. Frequent calls trying to transfer large amount of data using updateApplicationContext might fail, so for this usage call transferUserInfo instead.

transferUserInfo(:) and transferCurrentComplicationUserInfo(:) should be used if you want to send data in the background that needs to be received by the other application. Subsequent calls to this method are queued and all information sent from one app to the other is received. transferCurrentComplicationUserInfo might be used to send complication-related data to the WatchKit extension using a high-priority message and waking up the WatchKit app if needed. However, be aware that this function has a daily limit and once it's exceeded, data is transferred using the transferUserInfo function.

transferFile(_:metadata:) is similar in implementation and nature to transferUserInfo, but it accepts a fileURL instead of a dictionary as its input parameter and hence it should be used to send files local to the device to its counterpart. Subsequent calls are queued. Received files must be saved to a new location in the session(_:didReceive:) method, otherwise they are deleted.

sendMessage(:replyHandler:errorHandler:) and sendMessageData(:replyHandler:errorHandler:) send data immediately to a counterpart app. The counterpart app must be reachable before calling this method. The iOS app is always considered reachable, and calling this method from your Watch app wakes up the iOS app in the background as needed. The Watch app is considered reachable only while it is installed and running. The transfer must be initiated in the foreground. Subsequent calls to this method are queued.

For more information please see App programming guide for watchOS - Sharing Data.

Now some code examples:

Set up WatchConnectivity in the iOS app's AppDelegate:

import UIKit
import WatchConnectivity

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if WCSession.isSupported() {
let session = WCSession.default()
session.delegate = self
session.activate()
}
return true
}
}

extension AppDelegate: WCSessionDelegate {

func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
print("Message received: ",message)
}

//below 3 functions are needed to be able to connect to several Watches
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {}

func sessionDidDeactivate(_ session: WCSession) {}

func sessionDidBecomeInactive(_ session: WCSession) {}
}

Make your WatchKit class conform to WCSessionDelegate:

extension InterfaceController: WCSessionDelegate {
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {}
}

Using the instant messaging function, sendMessage:

In the WatchKit app use this code when you want to send information immediately to the iOS app.

if WCSession.isSupported() {
print("WCSession supported")
let session = WCSession.default()
session.delegate = self
session.activate()
if session.isReachable {
session.sendMessage(["Instant":"Message"], replyHandler: nil, errorHandler: { error in
print("Error sending message",error)
})
}
}

How to send data from Iphone to Apple Watch in OS2 in Objective-C



Here´s a link to a Q/A about WatchConnectivity: Send messages between iOS and WatchOS with WatchConnectivity in watchOS2


I will give you an example go ApplicationContext, there are 2 other messaging techniques with WatchConnectivity. Please watch WWDC2015 session video for those.

First you need to conform to the WCSessionDelegate protocol in the classes you want to send and receive data from/to. E.g both on watch and iPhone.

Basic checking before: (this is just an example, implement better than this)

if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
NSLog(@"SESSION AVAIBLE");
}

//Objective-C
if ([[WCSession defaultSession] isReachable]) {
NSLog(@"SESSION REACHABLE");
}

This will send the data from the phone to the watch.

WCSession *session = [WCSession defaultSession];
NSError *error;

[session updateApplicationContext:@{@"firstItem": @"item1", @"secondItem":[NSNumber numberWithInt:2]} error:&error];

This will receive the data from the phone on the watch.

- (void) session:(nonnull WCSession *)session didReceiveApplicationContext:(nonnull NSDictionary<NSString *,id> *)applicationContext {

NSLog(@"%@", applicationContext);


item1 = [applicationContext objectForKey:@"firstItem"];
item2 = [[applicationContext objectForKey:@"secondItem"] intValue];
}

The WWDC2015 video on WatchConnectivity is really great, I recommend to check it out.

WatchKit 2.0 Send Message From Phone To Watch

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.

Passing Data From A Class To WatchOS 2 (Connectivity)

Yes, that is an incorrect assumption. The WCSession sendMessage documentation states that the dictionary may only contain property list types which are basic types such as strings, integers, floats, data, etc.
So to send your content, either convert the object to a dictionary of key-value pairs or use the less recommended approach of using NSKeyedArchiver to convert your object directly to data.

WatchConnectivity - using sendMessage

All of the WCSession APIs that take a dictionary as a parameter only accept dictionaries of property list types; this includes the sendMessage API you are using:

message / A dictionary of property list values that you want to send. You define the contents of the dictionary that your counterpart supports. This parameter must not be nil.

So HKSamples are not a property list type which is why this isn't working, although you are saying the error handler is not getting invoked which sounds very suspicious. Are you certain changing your code to this doesn't log anything?

self.wcSession.sendMessage(applicationData, replyHandler: {(_: [String : AnyObject]) -> Void in
// handle reply from iPhone app here
}, errorHandler: {(error ) -> Void in
print(error);
})


Related Topics



Leave a reply



Submit