将WCSession与多个ViewController一起使用

时间:2015-09-14 22:32:35

标签: ios watchkit watch-os-2

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

任何人都可以在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我将显示心率和燃烧的卡路里。

4 个答案:

答案 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");
    }

}