在没有手表应用程序请求的情况下通知WatchKit应用程序
我知道WKInterfaceController openParentApplication
和handleWatchKitExtensionRequest
方法的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
最后,最后两个参数object
和suspensionBehaviour
我们使用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 } }
要做到这一点,你的手表应用程序需要在前台工作。
- 当试图在实际的Apple Watch上构build和运行WatchKit扩展时,如何避免“Error Launching”AppName“WatchKit Extension”错误?
- 无效Apple Watch图标文件名称必须匹配模式“* <dimension> @ <scale> x.png”
- 将数据从WatchKit中的模式视图传回
- 将数据传递给Apple Watch应用程序
- NSXMLParserErrorMessage – 无法打开数据stream
- 在Watch App中创buildWKInterfaceImage的进度圈
- 如何在iPhone应用程序已经显示通知的手表套件中显示通知
- 在Apple Watchkit中播放声音
- 无法在WatchKit中设置自定义字体