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

我知道WKInterfaceController openParentApplicationhandleWatchKitExtensionRequest方法的function,为手表应用程序打开父应用程序和发送/接收数据。

但如何呢…在用户使用父应用程序并在父应用程序中执行操作(即更改背景颜色)的情况下,我将如何立即通知手表应用程序并执行相关操作手表还?

在这个例子中,我相信MMWormhole就足够了,这是我应该采取的最好的方法还是有其他select?

背景

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

  • 在iPhone上运行的应用程序(我将它称为iPhone应用程序)
  • 在Watch上运行的应用程序
    • 在Watch上运行的UI
    • 在iPhone上作为扩展运行的代码。

首先和最后一行对我们来说是最重要的。 是的,扩展是运送到您的iPhone应用程序的AppStore,但是这两件事情可以在iOS操作系统中分开运行。 因此,扩展和iPhone应用程序是两个不同的进程 – 在OS中运行的两个不同的程序。

因为这个事实,我们不能使用[NSNotificationCenter defaultCenter]因为当你尝试在iPhone上使用NSLog() defaultCenter和在Extension中使用defaultCenter时,它们会有不同的内存地址。

达尔文来救援!

正如你可能想象的那样,这种问题对于开发人员来说并不陌生,适合的术语是进程间通信。 所以在OS X和iOS中有…达尔文通知机制。 而使用它的最简单的方法是从CFNotificationCenter类实现几个方法。

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

所以我们假设你想发送来自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的观察者? 为了完成我们的任务,我们需要创build一个循环,你会在一瞬间看到它。

至于第二种方法。

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

(__bridge const void *)(self) – 通知观察者

didReceivedDarwinNotification – callBack方法,当对象接收到通知时触发。 基本上和NSNotification中的@selector一样

CFSTR("NOTIFICATION_TO_WATCH") – 通知的名称,NSNotification中的同一个故事,但是在这里我们需要CFSTR方法将string转换为CFStringRef

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

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

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

现在,如您所见,此方法不以- (void)Name...开头。 为什么? 因为这是C方法。 你明白为什么我们需要NSNotificationCenter吗? 从C方法我们不能访问self 。 一个选项是声明自己的静态指针,如下所示: static id staticSelf赋值staticSelf = self然后使用它从((YourClass*)staticSelf)->_yourProperty((YourClass*)staticSelf)->_yourProperty但我认为NSNotificationCenter是更好的方法。

那么在select器响应你的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时忽略,最后一个参数是回答问题:立即交付?

以类似的方式,你可以发送通知从手表到iPhone。 从显而易见的原因,我build议使用不同的通知名称,如CFSTR("NOTIFICATION_TO_IPHONE")以避免的情况下,例如,iPhone发送通知到手表和自己。

总结一下

MMWormhole是完美的,写得很好的类,甚至包含大部分(如果不是全部)代码的testing。 这很容易使用,只记得以前设置你的AppGroups。 但是,如果您不想将第三方代码导入到项目中,或者您不想将其用于其他原因,则可以使用此答案中提供的实现。 特别是如果你不想/需要在iPhone和Watch之间交换数据。

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

希望这个帮助。

我相信你现在可能已经解决了你的问题。 但是用“watchOS 2”没有使用第三方课程,还有更好的方法。 您可以使用watch连接类的WCSession的sendMessage:replyHandler:errorHandler:方法。 即使您的iOS应用程序没有运行,它也可以工作。

而更多的信息,你可以参考这个博客。

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

首先,我在“awakeWithContext”方法中添加了观察者。 问题:通知给了好几次。 所以,我添加了一个观察者之前添加了“removeObserver:self”。 问题:当“自我”不同时,观察者不会被移除。 (另见这里 )

我结束了将下面的代码放在“willActivate”中:

 // 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 ); 

我还添加了以下内容到“didDeactivate”:

 [[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和一个通用的应用程序组( 更多信息 )来保存信息。 当Watch上的控制器激活时,它会检查NSUserDefaults,并在必要时更新视图。

有了WatchOS 2,你可以发送这样的消息方法;

家长应用程序

然后导入WatchConnectivity ;

将此添加到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 } 

然后在你的通知function;

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

要做到这一点,你的手表应用程序需要在前台工作。