I am trying to pass data from my app into my Apple Watch app. Basically, I am using the same method as I used for creating the today widget and so I am passing data through NSUserDefaults.
我正在尝试将我的应用中的数据传递到我的Apple Watch应用中。基本上,我使用的方法与我创建today小部件时使用的方法相同,所以我正在通过NSUserDefaults传递数据。
The problem is, that when I run my app, the data does not update the labels in the Watch app as I would expect it to.
问题是,当我运行我的应用程序时,数据不会像我期望的那样,在手表应用程序中更新标签。
Here is what I have...
这是我的……
override init(context: AnyObject?) {
// Initialize variables here.
super.init(context: context)
// Configure interface objects here.
NSLog("%@ init", self)
var defaults = NSUserDefaults(suiteName: "group.AffordIt")
var totalBudgetCalculation = ""
if (defaults!.stringForKey("totalBudgetWidget") != nil) {
println("Worked")
totalBudgetCalculation = defaults!.stringForKey("totalBudgetWidget")!
initialBudgetLabel.setText("Initial: \(totalBudgetCalculation)")
}
var currentBudgetCalculation = ""
if (defaults!.stringForKey("currentBudgetWidget") != nil) {
currentBudgetCalculation = defaults!.stringForKey("currentBudgetWidget")!
currentBudgetLabel.setText("Current: \(currentBudgetCalculation)")
}
}
I tried putting this code in willActivate()
, however that doesn't seem to make a difference.
我尝试将这段代码放到willActivate()中,但是这似乎没有什么区别。
Anyone know where I am going wrong?
有人知道我哪里出错了吗?
4 个解决方案
#1
25
I got it working using your method. I guess there's a couple of things you can check:
我用你的方法把它弄好了。我想有几件事你可以检查一下:
1) Are you synchronising the defaults after you set the value:
1)设置值后,是否同步默认值:
defaults?.synchronize();
NSLog("%@ ", defaults?.dictionaryRepresentation())
2) Have you enabled the App Group in both your app and your extension?
2)你是否在你的应用和扩展中都启用了应用组?
3) Are you using the correctly named app group when constructing the NSDefaults? For example, I use:
3)构建nsdefault时,是否使用正确命名的app组?例如,我使用:
NSUserDefaults(suiteName: "group.com.brindysoft.MyWatch");
Once all that's set up I run the app, set the value in the defaults, then run the glance target which reads the value from the default and that seems to work!
设置完后,我运行应用程序,在默认值中设置值,然后运行从默认值中读取值的glance目标,这似乎是有效的!
- Still stuck? check your app groups in your apple account
- 还困吗?在你的苹果账户里检查你的应用程序组
#2
20
The accepted answer applies to apple watch os 1. See NSUserDefaults not working on Xcode beta with Watch OS2
公认的答案适用于apple watch os 1。参见NSUserDefaults与Watch OS2在Xcode beta上工作
For OS2 - you will need to use the WatchConnectivity frameworks and implement the WCSessionDelegate.
对于OS2——您将需要使用WatchConnectivity框架并实现WCSessionDelegate。
import WatchConnectivity
import WatchKit
@available(iOS 9.0, *)
var alertDelegate:HomeIC? = nil
public class WatchData: NSObject,WCSessionDelegate {
var session = WCSession.defaultSession()
//
class var shared: WatchData {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: WatchData? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = WatchData()
}
return Static.instance!
}
public func session(session: WCSession, didReceiveFile file: WCSessionFile){
print(__FUNCTION__)
print(session)
}
public func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print(__FUNCTION__)
print(session)
alertDelegate?.showMessage("didReceiveApplicationContext")
}
public func sessionReachabilityDidChange(session: WCSession){
print(__FUNCTION__)
print(session)
print("reachability changed:\(session.reachable)")
let text = session.reachable ? "reachable" : "unreachable"
alertDelegate?.showMessage(text)
}
public func sessionWatchStateDidChange(session: WCSession) {
print(__FUNCTION__)
print(session)
print("reachable:\(session.reachable)")
// alertDelegate?.showMessage("sessionWatchStateDidChange")
if !session.receivedApplicationContext.keys.isEmpty {
alertDelegate?.showMessage(session.receivedApplicationContext.description)
}
}
public func session(session: WCSession, didReceiveMessageData messageData: NSData){
if !session.receivedApplicationContext.keys.isEmpty {
alertDelegate?.showMessage(session.receivedApplicationContext.description)
}
}
public func session(session: WCSession, didReceiveMessage message: [String : AnyObject]){
print(__FUNCTION__)
if let data = message["data"] {
alertDelegate?.showMessage(data as! String)
return
}
}
public func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
print(__FUNCTION__)
if let data = message["data"] {
alertDelegate?.showMessage(data as! String)
return
}
guard message["request"] as? String == "showAlert" else {return}
}
public func activate(){
if WCSession.isSupported() { // it is supported
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
print("watch activating WCSession")
} else {
print("watch does not support WCSession")
}
if(!session.reachable){
print("not reachable")
return
}else{
print("watch is reachable")
}
}
}
Sample Usage
示例使用
class HomeIC: WKInterfaceController {
// MARK: Properties
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Initialize the `WCSession`.
WatchData.shared.activate()
alertDelegate = self
}
internal func showMessage(msg:String){
let defaultAction = WKAlertAction(title: msg, style: WKAlertActionStyle.Default) { () -> Void in }
let actions = [defaultAction]
self.presentAlertControllerWithTitle( "Info", message: "", preferredStyle: WKAlertControllerStyle.Alert, actions: actions)
}
}
in my iphone code / I can invoke sharing data here
在我的iphone代码/我可以在这里调用共享数据
if #available(iOS 9.0, *) {
WatchData.shared.sendInbox()
} else {
// Fallback on earlier versions
}
And somewhere else I have another discrete singleton for watch data session.
在其他地方,我有另一个单独的用于观察数据会话的单例。
@available(iOS 9.0, *)
public class WatchData: NSObject,WCSessionDelegate {
var session = WCSession.defaultSession()
var payload:String = ""
class var shared: WatchData {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: WatchData? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = WatchData()
}
return Static.instance!
}
public func sessionReachabilityDidChange(session: WCSession){
print(__FUNCTION__)
print(session)
print("reachability changed:\(session.reachable)")
if (session.reachable){
}
}
public func sessionWatchStateDidChange(session: WCSession) {
print(__FUNCTION__)
print(session)
print("reachable:\(session.reachable)")
}
public func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
print(__FUNCTION__)
guard message["request"] as? String == "showAlert" else {return}
guard let m = message["m"] as? String else { return }
print("msg:",m)
}
public func sendInbox(){
if (!session.reachable){
if WCSession.isSupported() { // it is supported
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
print("iphone activating WCSession")
} else {
print("iphone does not support WCSession")
}
session.activateSession()
}
if(session.paired){
if(session.watchAppInstalled){
print("paired | watchAppInstalled")
}
}else{
print("not paired | or no watchAppInstalled")
}
if(!session.reachable){
print("not reachable")
return
}else{
/*let transfer:WCSessionUserInfoTransfer = (session.transferUserInfo(["data" : "Test2"]) as WCSessionUserInfoTransfer?)!
if(transfer.transferring){
print("-> iphone")
}else{
print("!-> iphone")
}*/
session.sendMessage(["data" :"test"],
replyHandler: { reply in
},
errorHandler: { error in
print(error)
})
}
}
}
Refer to sample watch os2 app
请参考示例watch os2应用程序
https://github.com/shu223/watchOS-2-Sampler/tree/20eeebeed66764d0814603e97d3aca5933236299
https://github.com/shu223/watchOS-2-Sampler/tree/20eeebeed66764d0814603e97d3aca5933236299
#3
10
As @johndpope said, shared NSUserDefaults no longer work on WatchOS2.
正如@johndpope所说,共享NSUserDefaults在WatchOS2上不再工作。
I'm posting a simplified solution that's not as full featured as john's but will get the job done in most cases.
我发布了一个简化的解决方案,它的功能不如约翰的,但在大多数情况下都能完成。
In your iPhone App, follow these steps:
在你的iPhone应用中,遵循以下步骤:
Pick find the view controller that you want to push data to the Apple Watch from and add the framework at the top.
选择要向Apple Watch推送数据的视图控制器,并在顶部添加框架。
import WatchConnectivity
Now, establish a WatchConnectivity session with the watch and send some data.
现在,与手表建立一个WatchConnectivity会话并发送一些数据。
if WCSession.isSupported() { //makes sure it's not an iPad or iPod
let watchSession = WCSession.defaultSession()
watchSession.delegate = self
watchSession.activateSession()
if watchSession.paired && watchSession.watchAppInstalled {
do {
try watchSession.updateApplicationContext(["foo": "bar"])
} catch let error as NSError {
print(error.description)
}
}
}
Please note, this will NOT work if you skip setting the delegate, so even if you never use it you must set it and add this extension:
请注意,如果您跳过设置委托,这将不起作用,所以即使您从未使用委托,您也必须设置它并添加这个扩展:
extension MyViewController: WCSessionDelegate {
}
Now, in your watch app (this exact code works for Glances and other watch kit app types as well) you add the framework:
现在,在你的手表应用程序(这个确切的代码也适用于扫瞄和其他手表工具包应用程序类型)中,你添加了框架:
import WatchConnectivity
Then you set up the connectivity session:
然后设置连接会话:
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
let watchSession = WCSession.defaultSession()
watchSession.delegate = self
watchSession.activateSession()
}
and you simply listen and handle the messages from the iOS app:
你只需聆听并处理来自iOS应用的消息:
extension InterfaceController: WCSessionDelegate {
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print("\(applicationContext)")
dispatch_async(dispatch_get_main_queue(), {
//update UI here
})
}
}
That's all there is to it.
这就是一切。
Items of note:
物品的注意:
- You can send a new applicationContext as often as you like and it doesn't matter if the watch is nearby and connected or if the watch app is running. This delivers the data in the background in an intelligent way and that data is sitting there waiting when the watch app is launched.
- 你可以像你喜欢的那样频繁地发送一个新的应用程序上下文,如果手表是在附近并且连接了或者手表应用程序正在运行,也没关系。它以一种智能的方式在后台传送数据,而当手表应用程序启动时,数据就在那里等待着。
- If your watch app is actually active and running, it should receive the message immediately in most cases.
- 如果你的手表应用程序实际上是活动和运行的,它应该在大多数情况下立即收到消息。
- You can reverse this code to have the watch send messages to the iPhone app the same way.
- 你可以反转这段代码,让手表以同样的方式向iPhone应用程序发送信息。
- applicationContext that your watch app receives when it is viewed will ONLY be the last message you sent. If you sent 20 messages before the watch app is viewed, it will ignore the first 19 and handle the 20th one.
- 您的手表应用程序在查看时接收的应用程序上下文将仅仅是您发送的最后一条消息。如果你在观看手表应用之前发送了20条信息,它会忽略前19条,处理第20条。
- For doing a direct/hard connection between the 2 apps or for background file transfers or queued messaging, check out the WWDC video.
- 为了在两个应用程序之间进行直接/硬连接,或者在后台文件传输或排队消息传递,请查看WWDC视频。
#4
6
Another way to communicate between the app and the watch is via wormhole:
另一种在应用程序和手表之间交流的方式是通过wormhole:
https://github.com/mutualmobile/MMWormhole
https://github.com/mutualmobile/MMWormhole
Send:
发送:
[self.wormhole passMessageObject:@{@"titleString" : title}
identifier:@"messageIdentifier"];
id messageObject = [self.wormhole messageWithIdentifier:@"messageIdentifier"];
Recieve:
接待:
[self.wormhole listenForMessageWithIdentifier:@"messageIdentifier"
listener:^(id messageObject) {
// Do Something
}];
#1
25
I got it working using your method. I guess there's a couple of things you can check:
我用你的方法把它弄好了。我想有几件事你可以检查一下:
1) Are you synchronising the defaults after you set the value:
1)设置值后,是否同步默认值:
defaults?.synchronize();
NSLog("%@ ", defaults?.dictionaryRepresentation())
2) Have you enabled the App Group in both your app and your extension?
2)你是否在你的应用和扩展中都启用了应用组?
3) Are you using the correctly named app group when constructing the NSDefaults? For example, I use:
3)构建nsdefault时,是否使用正确命名的app组?例如,我使用:
NSUserDefaults(suiteName: "group.com.brindysoft.MyWatch");
Once all that's set up I run the app, set the value in the defaults, then run the glance target which reads the value from the default and that seems to work!
设置完后,我运行应用程序,在默认值中设置值,然后运行从默认值中读取值的glance目标,这似乎是有效的!
- Still stuck? check your app groups in your apple account
- 还困吗?在你的苹果账户里检查你的应用程序组
#2
20
The accepted answer applies to apple watch os 1. See NSUserDefaults not working on Xcode beta with Watch OS2
公认的答案适用于apple watch os 1。参见NSUserDefaults与Watch OS2在Xcode beta上工作
For OS2 - you will need to use the WatchConnectivity frameworks and implement the WCSessionDelegate.
对于OS2——您将需要使用WatchConnectivity框架并实现WCSessionDelegate。
import WatchConnectivity
import WatchKit
@available(iOS 9.0, *)
var alertDelegate:HomeIC? = nil
public class WatchData: NSObject,WCSessionDelegate {
var session = WCSession.defaultSession()
//
class var shared: WatchData {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: WatchData? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = WatchData()
}
return Static.instance!
}
public func session(session: WCSession, didReceiveFile file: WCSessionFile){
print(__FUNCTION__)
print(session)
}
public func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print(__FUNCTION__)
print(session)
alertDelegate?.showMessage("didReceiveApplicationContext")
}
public func sessionReachabilityDidChange(session: WCSession){
print(__FUNCTION__)
print(session)
print("reachability changed:\(session.reachable)")
let text = session.reachable ? "reachable" : "unreachable"
alertDelegate?.showMessage(text)
}
public func sessionWatchStateDidChange(session: WCSession) {
print(__FUNCTION__)
print(session)
print("reachable:\(session.reachable)")
// alertDelegate?.showMessage("sessionWatchStateDidChange")
if !session.receivedApplicationContext.keys.isEmpty {
alertDelegate?.showMessage(session.receivedApplicationContext.description)
}
}
public func session(session: WCSession, didReceiveMessageData messageData: NSData){
if !session.receivedApplicationContext.keys.isEmpty {
alertDelegate?.showMessage(session.receivedApplicationContext.description)
}
}
public func session(session: WCSession, didReceiveMessage message: [String : AnyObject]){
print(__FUNCTION__)
if let data = message["data"] {
alertDelegate?.showMessage(data as! String)
return
}
}
public func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
print(__FUNCTION__)
if let data = message["data"] {
alertDelegate?.showMessage(data as! String)
return
}
guard message["request"] as? String == "showAlert" else {return}
}
public func activate(){
if WCSession.isSupported() { // it is supported
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
print("watch activating WCSession")
} else {
print("watch does not support WCSession")
}
if(!session.reachable){
print("not reachable")
return
}else{
print("watch is reachable")
}
}
}
Sample Usage
示例使用
class HomeIC: WKInterfaceController {
// MARK: Properties
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
// Initialize the `WCSession`.
WatchData.shared.activate()
alertDelegate = self
}
internal func showMessage(msg:String){
let defaultAction = WKAlertAction(title: msg, style: WKAlertActionStyle.Default) { () -> Void in }
let actions = [defaultAction]
self.presentAlertControllerWithTitle( "Info", message: "", preferredStyle: WKAlertControllerStyle.Alert, actions: actions)
}
}
in my iphone code / I can invoke sharing data here
在我的iphone代码/我可以在这里调用共享数据
if #available(iOS 9.0, *) {
WatchData.shared.sendInbox()
} else {
// Fallback on earlier versions
}
And somewhere else I have another discrete singleton for watch data session.
在其他地方,我有另一个单独的用于观察数据会话的单例。
@available(iOS 9.0, *)
public class WatchData: NSObject,WCSessionDelegate {
var session = WCSession.defaultSession()
var payload:String = ""
class var shared: WatchData {
struct Static {
static var onceToken: dispatch_once_t = 0
static var instance: WatchData? = nil
}
dispatch_once(&Static.onceToken) {
Static.instance = WatchData()
}
return Static.instance!
}
public func sessionReachabilityDidChange(session: WCSession){
print(__FUNCTION__)
print(session)
print("reachability changed:\(session.reachable)")
if (session.reachable){
}
}
public func sessionWatchStateDidChange(session: WCSession) {
print(__FUNCTION__)
print(session)
print("reachable:\(session.reachable)")
}
public func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
print(__FUNCTION__)
guard message["request"] as? String == "showAlert" else {return}
guard let m = message["m"] as? String else { return }
print("msg:",m)
}
public func sendInbox(){
if (!session.reachable){
if WCSession.isSupported() { // it is supported
session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
print("iphone activating WCSession")
} else {
print("iphone does not support WCSession")
}
session.activateSession()
}
if(session.paired){
if(session.watchAppInstalled){
print("paired | watchAppInstalled")
}
}else{
print("not paired | or no watchAppInstalled")
}
if(!session.reachable){
print("not reachable")
return
}else{
/*let transfer:WCSessionUserInfoTransfer = (session.transferUserInfo(["data" : "Test2"]) as WCSessionUserInfoTransfer?)!
if(transfer.transferring){
print("-> iphone")
}else{
print("!-> iphone")
}*/
session.sendMessage(["data" :"test"],
replyHandler: { reply in
},
errorHandler: { error in
print(error)
})
}
}
}
Refer to sample watch os2 app
请参考示例watch os2应用程序
https://github.com/shu223/watchOS-2-Sampler/tree/20eeebeed66764d0814603e97d3aca5933236299
https://github.com/shu223/watchOS-2-Sampler/tree/20eeebeed66764d0814603e97d3aca5933236299
#3
10
As @johndpope said, shared NSUserDefaults no longer work on WatchOS2.
正如@johndpope所说,共享NSUserDefaults在WatchOS2上不再工作。
I'm posting a simplified solution that's not as full featured as john's but will get the job done in most cases.
我发布了一个简化的解决方案,它的功能不如约翰的,但在大多数情况下都能完成。
In your iPhone App, follow these steps:
在你的iPhone应用中,遵循以下步骤:
Pick find the view controller that you want to push data to the Apple Watch from and add the framework at the top.
选择要向Apple Watch推送数据的视图控制器,并在顶部添加框架。
import WatchConnectivity
Now, establish a WatchConnectivity session with the watch and send some data.
现在,与手表建立一个WatchConnectivity会话并发送一些数据。
if WCSession.isSupported() { //makes sure it's not an iPad or iPod
let watchSession = WCSession.defaultSession()
watchSession.delegate = self
watchSession.activateSession()
if watchSession.paired && watchSession.watchAppInstalled {
do {
try watchSession.updateApplicationContext(["foo": "bar"])
} catch let error as NSError {
print(error.description)
}
}
}
Please note, this will NOT work if you skip setting the delegate, so even if you never use it you must set it and add this extension:
请注意,如果您跳过设置委托,这将不起作用,所以即使您从未使用委托,您也必须设置它并添加这个扩展:
extension MyViewController: WCSessionDelegate {
}
Now, in your watch app (this exact code works for Glances and other watch kit app types as well) you add the framework:
现在,在你的手表应用程序(这个确切的代码也适用于扫瞄和其他手表工具包应用程序类型)中,你添加了框架:
import WatchConnectivity
Then you set up the connectivity session:
然后设置连接会话:
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
let watchSession = WCSession.defaultSession()
watchSession.delegate = self
watchSession.activateSession()
}
and you simply listen and handle the messages from the iOS app:
你只需聆听并处理来自iOS应用的消息:
extension InterfaceController: WCSessionDelegate {
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print("\(applicationContext)")
dispatch_async(dispatch_get_main_queue(), {
//update UI here
})
}
}
That's all there is to it.
这就是一切。
Items of note:
物品的注意:
- You can send a new applicationContext as often as you like and it doesn't matter if the watch is nearby and connected or if the watch app is running. This delivers the data in the background in an intelligent way and that data is sitting there waiting when the watch app is launched.
- 你可以像你喜欢的那样频繁地发送一个新的应用程序上下文,如果手表是在附近并且连接了或者手表应用程序正在运行,也没关系。它以一种智能的方式在后台传送数据,而当手表应用程序启动时,数据就在那里等待着。
- If your watch app is actually active and running, it should receive the message immediately in most cases.
- 如果你的手表应用程序实际上是活动和运行的,它应该在大多数情况下立即收到消息。
- You can reverse this code to have the watch send messages to the iPhone app the same way.
- 你可以反转这段代码,让手表以同样的方式向iPhone应用程序发送信息。
- applicationContext that your watch app receives when it is viewed will ONLY be the last message you sent. If you sent 20 messages before the watch app is viewed, it will ignore the first 19 and handle the 20th one.
- 您的手表应用程序在查看时接收的应用程序上下文将仅仅是您发送的最后一条消息。如果你在观看手表应用之前发送了20条信息,它会忽略前19条,处理第20条。
- For doing a direct/hard connection between the 2 apps or for background file transfers or queued messaging, check out the WWDC video.
- 为了在两个应用程序之间进行直接/硬连接,或者在后台文件传输或排队消息传递,请查看WWDC视频。
#4
6
Another way to communicate between the app and the watch is via wormhole:
另一种在应用程序和手表之间交流的方式是通过wormhole:
https://github.com/mutualmobile/MMWormhole
https://github.com/mutualmobile/MMWormhole
Send:
发送:
[self.wormhole passMessageObject:@{@"titleString" : title}
identifier:@"messageIdentifier"];
id messageObject = [self.wormhole messageWithIdentifier:@"messageIdentifier"];
Recieve:
接待:
[self.wormhole listenForMessageWithIdentifier:@"messageIdentifier"
listener:^(id messageObject) {
// Do Something
}];