在多个ViewController中使用WCSession

我发现许多问题和许多答案,但没有最后的例子:

任何人都可以在Objective C中给出最后一个例子最好的做法是使用带有多个ViewController的IOS应用程序和Watch应用程序(WatchOS2)来使用WCSession。

我到目前为止注意到的是以下事实:

1.)在AppDelegate的父(IOS)应用程序中激活WCSession:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //Any other code you might have if ([WCSession isSupported]) { self.session = [WCSession defaultSession]; self.session.delegate = self; [self.session activateSession]; } } 

2.)在WatchOS2端使用<WCSessionDelegate> 。 但其余的对我来说完全不清楚! 一些答案正在通过指定的字典中的键来说话,如:

 [session updateApplicationContext:@{@"viewController1": @"item1"} error:&error]; [session updateApplicationContext:@{@"viewController2": @"item2"} error:&error]; 

其他人正在讨论检索默认会话

 WCSession* session = [WCSession defaultSession]; [session updateApplicationContext:applicationDict error:nil]; 

其他人在谈论不同的队列? “如果有必要,客户有责任派遣到另一个队列,然后发回主系统。”

我完全困惑。 所以,请举例说明如何将WCSession与IOS应用程序和具有多个ViewController的WatchOS2应用程序一起使用。

我需要它为以下情况(简化):在我的父母应用程序,我测量心率,锻炼时间和卡路里。 在Watch应用程序1. ViewController我将显示心率和锻炼时间在2. ViewController我也会显示心率,并消耗卡路里。

据我了解的任务,你只需要在Phone -> Watch方向同步,所以简而言之,你的最低configuration:

电话:

我相信application:didFinishLaunchingWithOptions:处理程序是WCSession初始化的最佳位置,因此将下面的代码放在那里:

 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]; } 

然后在你的代码中的某处测量一个心率例如:

 NSError *updateContextError; BOOL isContextUpdated = [[WCSession defaultSession] updateApplicationContext:@{@"heartRate": @"90"} error:&updateContextError] if (!isContextUpdated) { NSLog(@"Update failed with error: %@", updateContextError); } 

更新:

看:

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 

在你的Controller的某个地方,我们把它命名为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 

代码没有经过testing我只是写了,所以可能会有一些错别字。

我认为现在的主要想法是非常清楚的,剩下的数据types的转移并不是那么艰难的任务。

我目前的WatchConnectivity架构要复杂得多,但它是基于这个逻辑的。

如果您还有任何问题,我们可能会进一步讨论聊天。

那么,这是Greg Robertson所要求的解决scheme的简化版本。 对不起,它不在Objective-C中; 我只是从现有的AppStore批准的项目复制粘贴,以确保不会有错误。

实质上,任何WatchDataProviderDelegate都可以挂钩到数据提供者类,因为它为代表提供了数组持有者(而不是一个弱var)。 传入的WCSessionData使用notifyDelegates()方法转发给所有委托。

 // MARK: - Data Provider Class class WatchDataProvider: WCSessionDelegate { // This class is singleton static let sharedInstance = WatchDataProvider() // Sub-Delegates we'll forward to var delegates = [AnyObject]() init() { if WCSession.isSupported() { WCSession.defaultSession().delegate = self WCSession.defaultSession().activateSession() WatchDataProvider.activated = true; } } // MARK: - WCSessionDelegate public func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) { processIncomingMessage(userInfo) } public func session(session: WCSession, didReceiveApplicationContext applicationContext: [String: AnyObject]) { processIncomingMessage(applicationContext) } func processIncomingMessage(dictionary: [String:AnyObject] ) { // do something with incoming data< notifyDelegates() } // MARK: - QLWatchDataProviderDelegate public func addDelegate(delegate: AnyObject) { if !(delegates as NSArray).containsObject(delegate) { delegates.append(delegate) } } public func removeDelegate(delegate: AnyObject) { if (delegates as NSArray).containsObject(delegate) { delegates.removeAtIndex((delegates as NSArray).indexOfObject(delegate)) } } func notifyDelegates() { for delegate in delegates { if delegate.respondsToSelector("watchDataDidUpdate") { let validDelegate = delegate as! WatchDataProviderDelegate validDelegate.watchDataDidUpdate() } } } } // MARK: - Watch Glance (or any view controller) listening for changes class GlanceController: WKInterfaceController, WatchDataProviderDelegate { // A var in Swift is strong by default var dataProvider = WatchDataProvider.sharedInstance() // Obj-C would be: @property (nonatomic, string) WatchDataProvider *dataProvider override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) dataProvider.addDelegate(self) } // WatchDataProviderDelegate func watchDataDidUpdate() { dispatch_async(dispatch_get_main_queue(), { // update UI on main thread }) }} } class AnyOtherClass: UIViewController, WatchDataProviderDelegate { func viewDidLoad() { WatchDataProvider.sharedInstance().addDelegate(self) } // WatchDataProviderDelegate func watchDataDidUpdate() { dispatch_async(dispatch_get_main_queue(), { // update UI on main thread }) }} } 

在View-Controller中做会话pipe理(不过WCSession是单例)就像MVC违规(我已经看到太多的Watch博客文章错误了)。

我在WCSession上创build了一个雨伞单例类,这是第一个强烈引用的Watch Extension Delegate,以确保它很快加载,并且不会在工作中取消分配(例如,当View-Controller消失,而transferUserInfo或transferCurrentComplicationUserInfo发生时另一个手表线程)。

只有这个类才能处理/保存WCSession,并将会话数据(Model)从Watch应用程序中的所有View-Controller中分离出来,主要通过提供至less基本级别的线程安全性的公共静态类variables公开数据。

然后这个类被用于复杂控制器,扫视控制器和其他视图控制器。 更新在后台运行(或在backgroundFetchHandler中),应用程序(iOS / WatchOS)都不需要处于前台(如updateApplicationContext的情况下),并且会话不一定必须当前可到达。

我不是说这是理想的解决scheme,但是最终一旦我这样做就开始工作了。 我很乐意听到这是完全错误的,但是由于在采取这种方法之前我有很多问题,所以我现在就坚持下去。

我没有故意给出代码示例,因为它相当长,我不希望任何人盲目复制粘贴它。

我发现, “尝试错误”是一个解决scheme。 它正在工作,但我不知道为什么! 如果我从Watch上发送请求到IOS应用程序,那么Watch应用程序的ViewController的委托从IOS应用程序的主队列中获取所有数据。 我在- (void)awakeWithContext:(id)context添加了以下代码,并在Watch应用程序的所有ViewController中添加了- (void)willActivate

通过示例0 ViewController:

[self packageAndSendMessage:@ {@“request”:@“Yes”,@“counter”:[NSString stringWithFormat:@“%i”, 0 ]}];

通过示例1 ViewController1:

[self packageAndSendMessage:@ {@“request”:@“Yes”,@“counter”:[NSString stringWithFormat:@“%i”, 1 ]}];

 /* Helper function - accept Dictionary of values to send them to its phone - using sendMessage - including replay from phone */ -(void)packageAndSendMessage:(NSDictionary*)request { if(WCSession.isSupported){ WCSession* session = WCSession.defaultSession; session.delegate = self; [session activateSession]; if(session.reachable) { [session sendMessage:request replyHandler: ^(NSDictionary<NSString *,id> * __nonnull replyMessage) { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@".....replyHandler called --- %@",replyMessage); NSDictionary* message = replyMessage; NSString* response = message[@"response"]; [[WKInterfaceDevice currentDevice] playHaptic:WKHapticTypeSuccess]; if(response) NSLog(@"WK InterfaceController - (void)packageAndSendMessage = %@", response); else NSLog(@"WK InterfaceController - (void)packageAndSendMessage = %@", response); }); } errorHandler:^(NSError * __nonnull error) { dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"WK InterfaceController - (void)packageAndSendMessage = %@", error.localizedDescription); }); } ]; } else { NSLog(@"WK InterfaceController - (void)packageAndSendMessage = %@", @"Session Not reachable"); } } else { NSLog(@"WK InterfaceController - (void)packageAndSendMessage = %@", @"Session Not Supported"); } }