When to Call Activatesession() on Wcsession Object

When to call activateSession() on WCSession object

When you put the WCSession code in viewDidLoad and willActivate it is not only called when the app is opened but every time the view controller that contains the code is shown. So that is not an ideal place.

The best place to put this code is in application:didFinishLaunchingWithOptions in your app's AppDelegate and in applicationDidFinishLaunching in your watch extensions's ExtensionDelegate

You can put all the session handling into a singleton class, as suggested in this great tutorial by @NatashaTheRobot.

That way the session is only created once for the time the app in being held in memory.

EDIT

As ccjensen pointed out in his comment, if you are using the connection for a Complication, Notification or Glance update you have to activate the session in the ExtensionDelegate's init method. applicationDidFinishLaunching will not be called in those cases.

iOS WatchOS2 WatchConnectivity [WCSession defaultSession] activateSession] failure

I Solved this issue. The code is blow.

- (void)configSession{
dispatch_async(dispatch_get_main_queue(), ^{
if (![WCSession isSupported]) {
return;
}
_session = [WCSession defaultSession];
[_session setDelegate:self];
[_session activateSession];
});
}

But I don't see any documents that need to call this method in the main thread. Moreover, this method is called before is in the main thread.So I think it might be the runloop problem.

Using WCSession with more than one ViewController

As far as I understand the task you just need synchronisation in a Phone -> Watch direction so in a nutshell a minimum configuration for you:

Phone:

I believe the application:didFinishLaunchingWithOptions: handler is the best place for the WCSession initialisation therefore place the following code there:

if ([WCSession isSupported]) {
// You even don't need to set a delegate because you don't need to receive messages from Watch.
// Everything that you need is just activate a session.
[[WCSession defaultSession] activateSession];
}

Then somewhere in your code that measures a heart rate for example:

NSError *updateContextError;
BOOL isContextUpdated = [[WCSession defaultSession] updateApplicationContext:@{@"heartRate": @"90"} error:&updateContextError]

if (!isContextUpdated) {
NSLog(@"Update failed with error: %@", updateContextError);
}

update:

Watch:

ExtensionDelegate.h:

@import WatchConnectivity;
#import <WatchKit/WatchKit.h>

@interface ExtensionDelegate : NSObject <WKExtensionDelegate, WCSessionDelegate>
@end

ExtensionDelegate.m:

#import "ExtensionDelegate.h"

@implementation ExtensionDelegate

- (void)applicationDidFinishLaunching {
// Session objects are always available on Apple Watch thus there is no use in calling +WCSession.isSupported method.
[WCSession defaultSession].delegate = self;
[[WCSession defaultSession] activateSession];
}

- (void)session:(nonnull WCSession *)session didReceiveApplicationContext:(nonnull NSDictionary<NSString *,id> *)applicationContext {
NSString *heartRate = [applicationContext objectForKey:@"heartRate"];

// Compose a userInfo to pass it using postNotificationName method.
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:heartRate forKey:@"heartRate"];

// Broadcast data outside.
[[NSNotificationCenter defaultCenter] postNotificationName: @"heartRateDidUpdate" object:nil userInfo:userInfo];
}

@end

Somewhere in your Controller, let's name it XYZController1.

XYZController1:

#import "XYZController1.h"

@implementation XYZController1

- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleUpdatedHeartRate:) name:@"heartRateDidUpdate" object:nil];
}

-(void)handleUpdatedHeartRate:(NSNotification *)notification {
NSDictionary* userInfo = notification.userInfo;
NSString* heartRate = userInfo[@"heartRate"];
NSLog (@"Successfully received heartRate notification!");
}

@end

Code hasn't been tested I just wrote it as is so there can be some typos.

I think the main idea now is quite clear and a transfer of remaining types of data is not that tough task.

My current WatchConnectivity architecture much more complicated but nevertheless it is based on this logic.

If you still have any questions we might move a further discussion to the chat.

Apple Watch WCConnectionDelegate, sending message in activationDidComplete fails occasionally?

In my experience watch OS is pretty finicky, especially when using older model watches. That being said I think the answer to the question: "Is it unsafe to assume the session is ready to accept communication in activationDidCompleteWith?" is yes, it is unsafe to assume that.

In my own app I have a very similar case to yours and I solved it by sending a message until a response is received.

    // false until a response is received from the phone
let receivedResponse: Bool = false

// function that sends the message
func requestResponse() {
guard WCSession.default.isReachable else {
print("Phone not reachable")
return
}

// callback that handles response
let responseHandler: ([String: Any]) -> () = { response in
receivedResponse = true
callback(response)
}

WCSession.default.sendMessage(["Request": "Response"],
replyHandler: responseHandler) { error in
print(error.localizedDescription)
}
}

// timer that calls the request function repeatedly
let retryTimer = Timer.scheduledTimer(withTimeInterval: 1,
repeats: true) { timer in
if receivedResponse {
// we know we got a response so clean up timer
timer.invalidate()
}
requestResponse()
}

WCSession transferUserInfo only works in Foreground

I just watched the Watch Connectivity session from WWDC 2015. It seems that transferUserInfo cannot be received by iOS until the app is in the foreground. That is of course what I am seeing with actual devices. The issue here then, and what has thrown me off, is that the simulator as of this writing DOES receive these messages when in the background. This is not the correct behavior and should therefore be considered a bug in the functioning of the simulator.

For my purposes I should be able to use sendMessagefrom the watch to iOS when iOS is in the background. However, the same is not true in reverse. To use sendMessage from iOS to the watch, the watch will have to be in the foreground.

Calling func session didReceiveMessage in AppDelegate

If your iPhone app is closed and you have implemented the

 func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
}

in a specific VC you won't get the message until your VC is live but if you have implemented the above method in AppDelegate , you will received it even when your app is closed.

As mentioned in the link

Calling this method from your WatchKit extension while it is active
and running wakes up the corresponding iOS app in the background and
makes it reachable. Calling this method from your iOS app does not
wake up the corresponding WatchKit extension

Your app did wake up in background but since your VC is not live that's why you are not getting the message.



Related Topics



Leave a reply



Submit