Using Wcsession with More Than One Viewcontroller

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.

WatchConnectivity how to share session among multiple WKInterfaceControllers?

The other answer doesn't explain that an app-wide session would work.

You can use an app-wide WCSession singleton which would be available to all your interface controllers. You simply instantiate a session manager very early in the app life cycle, and let it be its own delegate.

Instead of trying to make each interface controller handle the session delegation (which won't work well), a session manager (singleton) can handle all the transfers for your interface controllers.

As mentioned in the other answer, you could then use notifications to let interested interface controllers know when their new data arrives.

Using a modular approach, such as a session or data manager, helps to keep such code out of a controller, where it really doesn't belong. It also makes it easier to test and utilize each module.

I won't repeat the code here, as there have already been several existing answers posted on Stack Overflow, as well as
articles on the web, which cover this technique in detail. For example:

  • Using WCSession with more than one ViewController

  • WatchConnectivity: Say Hello to WCSession

You'll often find these types of answers within narrower questions that ask how to share data between, say, a watch app and its complication controller.

Send and receive messages from iPhone to Watch from multiple Controllers

I'd suggest removing all data management away from the controllers that handle your UI. It is a poor design and will likely cause you headaches later to mix the layers like this.

You should instead have a data manager that is the WCSession delegate and takes care of persisting the information, and then notifying the relevant parties (view controllers, etc) that the backing data has been updated.

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.

setting delegate of WCSession to nil

WCSession.delegate won't leak: it is a weak reference

NS_CLASS_AVAILABLE_IOS(9.0)
@interface WCSession : NSObject
// ...
/** A delegate must exist before the session will allow sends. */
@property (nonatomic, weak, nullable) id <WCSessionDelegate> delegate;
// ...

If you're using ARC and your delegate is still being held on memory, it is not because of WCSession.delegate



Related Topics



Leave a reply



Submit