我了解监视应用程序打开父应用并发送/接收数据的WKInterfaceController openParentApplication
和handleWatchKitExtensionRequest
方法的功能。
但是如何...在用户使用父应用程序并在父应用程序中执行操作(即更改背景颜色)的实例中,如何立即通知监视应用程序并执行关于手表的相关行动呢?
我相信MMWormhole在这个例子中就足够了,这是我应该采取的最佳方法还是有替代方案?
答案 0 :(得分:39)
首先,让我们总结一下我们所知道的事情。 我们有
第一行和最后一行对我们来说最重要。是的,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
最后两个参数object
和suspensionBehaviour
- 在我们使用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需要在前台工作。