我发现了许多问题和许多答案,但没有最后的例子请求:
任何人都可以在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我将显示心率和燃烧的卡路里。
答案 0 :(得分:9)
据我了解这项任务,您只需要在Phone -> Watch
方向进行同步,所以简而言之,最低配置为您:
<强>电话:强>
我相信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
代码尚未经过测试我只是按原样编写,因此可能存在一些拼写错误。
我认为现在的主要思路非常明确,剩余数据类型的转移并不是一项艰巨的任务。
我目前的WatchConnectivity架构要复杂得多,但它基于这种逻辑。
如果您还有任何问题,我们可以进一步讨论聊天。
答案 1 :(得分:4)
嗯,这是Greg Robertson要求的我的解决方案的简化版本。对不起,它不再是Objective-C了;我只是从现有AppStore批准的项目中复制粘贴,以确保不会出错。
基本上,任何WatchDataProviderDelegate都可以挂钩到数据提供程序类,因为它为委托提供了数组持有者(而不是一个弱变量)。 传入的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
})
}}
}
答案 2 :(得分:3)
在View-Controller中进行会话管理(但是WCSession是单例)闻起来像MVC违规(我已经看到太多Watch博客帖子已经错了)。
我在WCSession上创建了一个伞形单例类,它首先从Watch Extension Delegate中强烈引用,以确保它很快就会加载,并且在工作中不会被释放(例如,当transferUserInfo或视图控制器消失时) transferCurrentComplicationUserInfo发生在另一个监视线程中)。
然后只有这个类处理/保存WCSession并将会话数据(Model)与watch app中的所有View-Controller分离,主要通过提供至少基本线程安全性的公共静态类变量来公开数据
然后从复杂控制器,扫视控制器和其他视图控制器使用此类。更新在后台(或在backgroundFetchHandler中)运行,所有应用程序(iOS / WatchOS)都不需要在前台(如在updateApplicationContext的情况下),并且会话不一定必须当前可以访问。
我不是说这是理想的解决方案,但最后一旦我这样做就开始工作了。我很想听到这是完全错误的,但由于我在采用这种方法之前遇到了很多问题,我现在就会坚持下去。
我没有故意提供代码示例,因为它很长,而且我不希望任何人盲目地复制粘贴它。
答案 3 :(得分:0)
我找到了“尝试和错误”,一个解决方案。它有效,但我不确切知道为什么!如果我将Watch的请求发送到IOS应用程序,Watch应用程序的ViewController的代理将从IOS应用程序获取主队列中的所有数据。我在Watch应用的所有ViewControllers的- (void)awakeWithContext:(id)context
和- (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");
}
}