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
Confirm Back Button on Uinavigationcontroller
How to Get Current Location Using Cllocationmanager in iOS
Phimageresultisdegradedkey/Phimagefileurlkey Is Not Found
Best Method to Store Data for an iOS App
iOS Web App: Showing Content Only If the Application Is Standalone
How to Encrypt and Decrypt a String(Plain Text) with Rsa Public Key in iOS, Swift
Extract Last Word in String with Swift
Disable Warning Dialog If Bluetooth Is Powered Off iOS
iOS 7.0 and Arc: Uitableview Never Deallocated After Rows Animation
iOS 11 - Unable to Change Navigation Bar Height
Making Uitableview with Embedded Uicollectionview Using Uitableviewautomaticdimension
iPad 3 - Opengl Bug with Keagldrawablepropertyretainedbacking and Retina
How to Take a Snapshot of a Uiview That Isn't Rendered
Drawing a Polygon with One Color for Stroke, and a Different One for Fill