我了解WKInterfaceController openParentApplication
和handleWatchKitExtensionRequest
方法的能力,可以使手表应用程序打开父应用程序并发送/接收数据。
但是,如果用户在使用父应用程序并执行操作(例如更改背景颜色),如何立即通知手表应用程序并在手表上执行相关操作?
我认为MMWormhole在这种情况下就足够了,这是我应该采取的最佳方法还是有替代方案?
我了解WKInterfaceController openParentApplication
和handleWatchKitExtensionRequest
方法的能力,可以使手表应用程序打开父应用程序并发送/接收数据。
但是,如果用户在使用父应用程序并执行操作(例如更改背景颜色),如何立即通知手表应用程序并在手表上执行相关操作?
我认为MMWormhole在这种情况下就足够了,这是我应该采取的最佳方法还是有替代方案?
首先,让我们总结一下我们所知道的。我们有:
对于我们来说,第一个和最后一行是最重要的。是的,扩展与 iPhone 应用程序一起发布到 AppStore 中,但是这两个东西可以在 iOS 操作系统中分别运行。因此,扩展和 iPhone 应用程序是两个不同的进程 - 在操作系统中运行的两个不同程序。
由于这个事实,我们不能使用[NSNotificationCenter defaultCenter]
,因为当您尝试在 iPhone 上使用 NSLog()
defaultCenter,并在扩展中使用 defaultCenter 时,它们将具有不同的内存地址。
正如您可能想象的那样,这种问题对于开发人员来说并不新鲜,它的正确术语是进程间通信。因此,在 OS X 和 iOS 中有 Darwin 通知机制。而使用它的最简单方法是实现CFNotificationCenter
类中的几个方法。
当使用 CFNotificationCenter 时,您会发现它看起来非常类似于 NSNotificationCenter。我猜 NSNotif.. 是围绕 CFNotif.. 构建的,但我没有证实这个假设。现在,到重点。
所以让我们假设您想要在 iPhone 和 Apple Watch 之间相互发送通知。我们首先应该做的是注册通知。
- (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()
- 获取Darwin Notify中心
(__bridge const void *)(self)
- 通知观察者
didReceivedDarwinNotification
- 回调方法,在对象接收通知时触发。
基本上与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是更好的方法。
所以,在响应你的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发送通知。出于明显的原因,我建议使用不同的通知名称,例如CFSTR("NOTIFICATION_TO_IPHONE")
,以避免出现这样的情况,例如iPhone向手表和自己发送通知。
MMWormhole
是完全正常且编写良好的类,甚至包含了大多数(如果不是全部)代码的测试。它很容易使用,只需记住在设置之前设置AppGroups。
但是,如果您不想将第三方代码导入项目或由于其他原因而不想使用它,则可以使用本答案提供的实现。
特别是如果您不想/需要在iPhone和Watch之间交换数据。
还有第二个不错的项目LLBSDMessaging
。它基于Berkeley套接字。更复杂,基于更底层的代码。这是链接到冗长但编写良好的博客文章,您将在那里找到Github的链接。 http://ddeville.me/2015/02/interprocess-communication-on-ios-with-berkeley-sockets/。
希望这有所帮助。
// 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 );
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"com.toWatch.todo.updated" object:nil];
CFNotificationCenterRemoveObserver( CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)( self ), CFSTR( "NOTIFICATION_TO_WATCH_TODO_UPDATED" ), NULL );
在 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
}
}
为了实现这一点,你的手表应用需要在前台运行。