在没有手表应用程序请求的情况下通知WatchKit应用程序更新

时间:2015-03-02 11:57:06

标签: ios watchkit apple-watch

我了解监视应用程序打开父应用并发送/接收数据的WKInterfaceController openParentApplicationhandleWatchKitExtensionRequest方法的功能。

但是如何...在用户使用父应用程序并在父应用程序中执行操作(即更改背景颜色)的实例中,如何立即通知监视应用程序并执行关于手表的相关行动呢?

我相信MMWormhole在这个例子中就足够了,这是我应该采取的最佳方法还是有替代方案?

4 个答案:

答案 0 :(得分:39)

背景

首先,让我们总结一下我们所知道的事情。 我们有

  • 在iPhone上运行的应用程序(我将其称为 iPhone应用程序)
  • 在Watch上运行的app ...具体来说
    • 在Watch上运行的用户界面
    • 在iPhone上作为扩展程序运行的代码。

第一行和最后一行对我们来说最重要。是的,Extension通过您的iPhone应用程序发送到AppStore,但是这两件事可以在iOS操作系统中单独运行。因此,Extension和iPhone应用程序是两个不同的进程 - 在OS中运行的两个不同的程序。

由于这个原因,我们无法使用[NSNotificationCenter defaultCenter],因为当您尝试在iPhone上使用NSLog() defaultCenter和在Extension中使用defaultCenter时,它们将具有不同的内存地址。

达尔文救援!

正如您可能想象的那样,这类问题对开发人员来说并不陌生,它的正确用语是进程间通信。所以在OS X和iOS中有... Darwin Notification机制。使用它的最简单方法是从CFNotificationCenter类实现几个方法。

实施例

使用CFNotificationCenter时,您会发现它与NSNotificationCenter非常相似。我的猜测是NSNotif ..是围绕CFNotif构建的......但我没有证实这个假设。现在,重点。

所以我们假设您想要从iPhone发送通知来回观看。我们首先应该注册通知。

- (void)registerToNotification
{    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceivedNSNotification) name:@"com.example.MyAwesomeApp" object:nil];

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), didReceivedDarwinNotification, CFSTR("NOTIFICATION_TO_WATCH"), NULL, CFNotificationSuspensionBehaviorDrop);
}

您可能想知道为什么我添加了NSNotificationCenter的观察者?为了完成我们的任务,我们需要创建一些循环,你马上就会看到它。

至于第二种方法。

CFNotificationCenterGetDarwinNotifyCenter() - 获取达尔文通知中心

(__bridge const void *)(self) - 通知观察员

didReceivedDarwinNotification - 当对象收到通知时触发的callBack方法。 基本上它与NSNotification中的@selector相同

CFSTR("NOTIFICATION_TO_WATCH") - 通知名称,NSNotification中的相同故事,但这里我们需要CFSTR方法将字符串转换为CFStringRef

最后两个参数objectsuspensionBehaviour - 在我们使用DarwinNotifyCenter时都被忽略了。

很酷,所以我们注册为观察员。因此,让我们实现我们的回调方法(其中有两个,一个用于CFNotificationCenter,一个用于NSNotificationCenter)。

void didReceivedDarwinNotification()
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"com.example.MyAwesomeApp" object:nil];
}

现在,如您所见,此方法并非以- (void)Name...开头。为什么?因为它的C方法。你知道我们为什么需要NSNotificationCenter吗?从C方法我们无法访问self。一种选择是向自己声明静态指针,如下所示:static id staticSelf为其分配staticSelf = self然后从didReceivedDarwinNotification使用它:((YourClass*)staticSelf)->_yourProperty但我认为NSNotificationCenter是更好的方法。< / p>

然后在选择器中响应你的NSNotification:

- (void)didReceivedNSNotification
{
    // you can do what you want, Obj-C method
}

当我们最终注册为观察员时,我们可以从iPhone应用程序发送内容。

为此,我们只需要一行代码。

CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("NOTIFICATION_TO_WATCH"), (__bridge const void *)(self), nil, TRUE);

可以在您的ViewController或Model。

同样,我们希望获得CFNotificationCenterGetDarwinNotifyCenter(),然后我们指定通知名称,发布通知的对象,字典对象(使用DarwinNotifyCenter时忽略,最后一个参数回答问题:立即发送?

以类似的方式,您可以从Watch向iPhone发送通知。由于显而易见的原因,我建议使用不同的通知名称,例如CFSTR("NOTIFICATION_TO_IPHONE"),以避免例如iPhone向Watch及其自身发送通知的情况。

总结

MMWormhole是完美的,编写得很好的类,即使测试覆盖了大多数(如果不是全部)代码。它易于使用,只需记住之前设置您的AppGroups。 但是,如果您不想将第三方代码导入项目,或者由于某些其他原因而不想使用它,则可以使用此答案中提供的实现。 特别是如果您不想/需要在iPhone和Watch之间交换数据。

还有第二个好项目LLBSDMessaging。它基于伯克利插座。更复杂,基于更低级别的代码。这里链接到冗长但写得很好的博客文章,你会在那里找到Github的链接。 http://ddeville.me/2015/02/interprocess-communication-on-ios-with-berkeley-sockets/

希望得到这个帮助。

答案 1 :(得分:2)

我相信你现在可能已经解决了你的问题。但是使用&#34; watchOS 2&#34;没有使用第三方课程,有更好的方法。 您可以使用sendMessage:replyHandler:errorHandler: Watch Connectivity Class的WCSession方法。即使您的iOS应用未运行,它也能正常运行。

有关详细信息,请参阅this blog.

答案 2 :(得分:1)

以上Ivp的回答很好。但是,我想补充一点,使用通知可能很棘手,我想分享我的经验。

首先,我在方法&#34; awakeWithContext&#34;中添加了观察者。问题:通知已多次发出。所以,我添加了#34; removeObserver:self&#34;在添加观察者之前。问题:当&#34; self&#34;时,观察者不会被移除。是不同的。 (另见here。)

我最终将以下代码放入&#34; willActivate&#34;:

// make sure the the observer is not added several times if this function gets called more than one time
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"com.toWatch.todo.updated" object:nil];
CFNotificationCenterRemoveObserver( CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)( self ), CFSTR( "NOTIFICATION_TO_WATCH_TODO_UPDATED" ), NULL );

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( didReceivedNSNotificationTodo ) name:@"com.toWatch.todo.updated" object:nil];
CFNotificationCenterAddObserver( CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)( self ), didReceivedDarwinNotificationTodo, CFSTR( "NOTIFICATION_TO_WATCH_TODO_UPDATED" ), NULL, CFNotificationSuspensionBehaviorDrop );

我还在&#34; didDeactivate&#34;:

中添加了以下内容
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"com.toWatch.todo.updated" object:nil];
CFNotificationCenterRemoveObserver( CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)( self ), CFSTR( "NOTIFICATION_TO_WATCH_TODO_UPDATED" ), NULL );

如果Watch应用程序处于非活动状态时发送通知,则不会发送此通知。

因此,除了上面的通知机制,它可以通知活动的Watch应用程序在iPhone上进行的更改,我使用NSUserDefaults和一个通用应用程序组(more info)来保存信息。当Watch上的控制器激活时,它会检查NSUserDefaults并在必要时更新视图。

答案 3 :(得分:0)

使用WatchOS 2,您可以sendMessage这样的方法;

家长应用

导入WatchConnectivity然后;

将此添加到AppDelegate中的didFinishLaunchingWithOptions方法;

if #available(iOS 9.0, *) {
    if WCSession.isSupported() {
        let session = WCSession.defaultSession()
        session.delegate = self
        session.activateSession()

        if !session.paired {
            print("Apple Watch is not paired")
        }
        if !session.watchAppInstalled {
            print("WatchKit app is not installed")
        }
    } else {
        print("WatchConnectivity is not supported on this device")
    }
} else {
     // Fallback on earlier versions
}

然后在你的通知功能中;

func colorChange(notification: NSNotification) {
     if #available(iOS 9.0, *) {
        if WCSession.defaultSession().reachable {
           let requestValues = ["color" : UIColor.redColor()]
           let session = WCSession.defaultSession()

           session.sendMessage(requestValues, replyHandler: { _ in
                    }, errorHandler: { error in
                        print("Error with sending message: \(error)")
                })
            } else {
                print("WCSession is not reachable to send data Watch App from iOS")
            }
     } else {
         print("Not available for iOS 9.0")
     }
 }

观看应用

不要忘记导入WatchConnectivity并将WCSessionDelegate添加到InterfaceController

override func awakeWithContext(context: AnyObject?) {
    super.awakeWithContext(context)

    // Create a session, set delegate and activate it
    if (WCSession.isSupported()) {
        let session = WCSession.defaultSession()
        session.delegate = self
        session.activateSession()
    } else {
        print("Watch is not supported!")
    }
}

func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) { 
    if let deviceColor = message["color"] as? UIColor {
        // do whatever you want with color
    }
}

为此,您的Watch App需要在前台工作。