NSUserDefaults 不适用于 Watch OS2 的 Xcode beta
Posted
技术标签:
【中文标题】NSUserDefaults 不适用于 Watch OS2 的 Xcode beta【英文标题】:NSUserDefaults not working on Xcode beta with Watch OS2 【发布时间】:2015-08-31 08:03:46 【问题描述】:我刚刚安装了最新的 Xcode 测试版来试用 Swift 2 以及对 Apple Watch 开发部分所做的改进。
我实际上很难弄清楚为什么这种在 iOS 和 Watch OS2 之间共享信息的基本 NSUserDefaults
方法不起作用。
我关注this step-by-step tutorial 以检查我是否在此过程中遗漏了某些内容,例如为电话应用程序和分机打开同一个组,但这是我得到的:NOTHING。 p>
这是我在 iPhone 应用程序中为 ViewController 编写的内容:
import UIKit
class ViewController: UIViewController
@IBOutlet weak var lb_testo: UITextField!
let shared_defaults:NSUserDefaults = NSUserDefaults(suiteName: "group.saracanducci.test")!
var name_data:NSString? = ""
override func viewDidLoad()
super.viewDidLoad()
name_data = shared_defaults.stringForKey("shared")
lb_testo.text = name_data as? String
override func didReceiveMemoryWarning()
super.didReceiveMemoryWarning()
@IBAction func upgrade_name(sender: AnyObject)
name_data = lb_testo.text
shared_defaults.setObject(name_data, forKey: "shared")
lb_testo.resignFirstResponder()
shared_defaults.synchronize()
这就是我在 WatchKit 的 InterfaceController 中的内容:
import WatchKit
import Foundation
class InterfaceController: WKInterfaceController
@IBOutlet var lb_nome: WKInterfaceLabel!
let shared_defaults:NSUserDefaults = NSUserDefaults(suiteName: "group.saracanducci.test")!
var name_data:NSString? = ""
override func awakeWithContext(context: AnyObject?)
super.awakeWithContext(context)
override func willActivate()
super.willActivate()
if (shared_defaults.stringForKey("shared") != "")
name_data = shared_defaults.stringForKey("shared")
lb_nome.setText(name_data as? String)
else
lb_nome.setText("No Value")
override func didDeactivate()
super.didDeactivate()
我做了一些测试,似乎 ios 应用和 Watch OS 使用了不同的组......他们不共享信息,他们将信息存储在本地。
有人遇到同样的问题吗?知道如何解决吗?
【问题讨论】:
请在问题中包含您的代码,以免损坏的链接不会使该问题对未来的读者毫无用处。 .stringForKey("shared") 已经返回了一个不需要转换的字符串。您应该使用 nil 合并运算符来解开它 name_data = NSUSerDefaults().stringForKey("shared") ?? “没有价值” 问题肯定出在其他地方。您拥有的代码是正确的;我在我的 3 个手表应用程序中多次使用这种模式。我认为这不会有帮助,但请尝试将 NSUserDefaults 调用移动到您的数据源,以便您从一个地方获取 NSUserDefaults 引用,而不是初始化它的 2 个不同副本。 我会在标题中添加 xcode 7 / watch OS2。 对于那些感兴趣的人,即使在 watchOS 2 中,设置仍会从 iOS 推送到 Apple Watch。请参阅我的回答 ***.com/a/32707727/630614 【参考方案1】:使用 watch OS2,您不能再使用共享组容器。 Apple Docs:
观看使用共享组与其 iOS 应用共享数据的应用 必须重新设计容器以不同方式处理数据。在 watchOS 2 中, 每个进程必须在本地管理自己的任何共享数据的副本 容器目录。对于实际共享和更新的数据 这两个应用程序,这需要使用 Watch Connectivity 框架来 在它们之间移动数据。
【讨论】:
谢谢伙计,这段代码我快疯了!我会想出另一种使用 Watch Connectivity 使其工作的方法 :) 我也会这样做;) OMG - 最近几天真是浪费时间。 谢谢你救了我的命。 卫生署! - 今晚我想“嘿,我为什么不重新设计我的应用程序以使用共享应用程序组”,然后我花了几个小时试图找出它为什么不起作用。最糟糕的部分?显然我两周前看到并接受了这个答案!【参考方案2】:NSUserDefaults(即使有应用组)不会在 watchOS 2 中的 iPhone 和 Watch 之间同步。如果你想从你的 iPhone 应用或 Settings-Watch.bundle 同步设置,你必须处理同步自己。
我发现在这种情况下,使用 WatchConnectivity 的用户信息传输非常有效。 您将在下面找到如何实现此功能的示例。该代码仅处理从手机到 Watch 的单向同步,但另一种方式的工作方式相同。
在 iPhone 应用程序中: 1) 准备需要同步的设置字典
- (NSDictionary *)exportedSettingsForWatchApp
NSUserDefaults *userDefaults = [self userDefaults]; // the user defaults to sync
NSSet *keys = [self userDefaultKeysForWatchApp]; // set of keys that need to be synced
NSMutableDictionary *exportedSettings = [[NSMutableDictionary alloc] initWithCapacity:keys.count];
for (NSString *key in keys)
id object = [userDefaults objectForKey:key];
if (object != nil)
[exportedSettings setObject:object forKey:key];
return [exportedSettings copy];
2) 确定何时需要将设置推送到 Watch (此处未显示)
3) 将设置推送到手表
- (void)pushSettingsToWatchApp
// Cancel current transfer
[self.outstandingSettingsTransfer cancel];
self.outstandingSettingsTransfer = nil;
// Cancel outstanding transfers that might have been started before the app was launched
for (WCSessionUserInfoTransfer *userInfoTransfer in self.session.outstandingUserInfoTransfers)
BOOL isSettingsTransfer = ([userInfoTransfer.userInfo objectForKey:@"settings"] != nil);
if (isSettingsTransfer)
[userInfoTransfer cancel];
// Mark the Watch as requiring an update
self.watchAppHasSettings = NO;
// Only start a transfer when the watch app is installed
if (self.session.isWatchAppInstalled)
NSDictionary *exportedSettings = [self exportedSettingsForWatchApp];
if (exportedSettings == nil)
exportedSettings = @ ;
NSDictionary *userInfo = @ @"settings": exportedSettings ;
self.outstandingSettingsTransfer = [self.session transferUserInfo:userInfo];
在观看扩展中: 4) 接收用户信息传输
- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo
NSDictionary *settings = [userInfo objectForKey:@"settings"];
if (settings != nil)
// Import the settings
[self importSettingsFromCompanionApp:settings];
5) 将收到的设置保存到手表上的用户默认设置
- (void)importSettingsFromCompanionApp:(NSDictionary *)settings
NSUserDefaults *userDefaults = [self userDefaults]; // the user defaults to sync
NSSet *keys = [self userDefaultKeysForWatchApp]; // set of keys that need to be synced
for (NSString *key in keys)
id object = [settings objectForKey:key];
if (object != nil)
[userDefaults setObject:object forKey:key];
else
[userDefaults removeObjectForKey:key];
[userDefaults synchronize];
【讨论】:
有机会发布 swift 版本吗? 不,对不起。但是自己移植应该不难。 见这里快速连接代码developer.apple.com/videos/play/wwdc2015-713 我看到 [self userdefaults];我可以在这里使用 [NSUserDefaults standardUserDefaults] 吗? 是的。就我而言,我使用的是组用户默认值,这就是存在 [self userDefaults] 的原因。【参考方案3】:有一种重现旧功能的简单方法,我将旧组用户默认值导出到字典中,将其发送到 WatchConnectivity 框架,然后将它们重新导入另一端的用户默认值:
在电话和手表应用中:
-
添加 WatchConnectivty 框架
#import <WatchConnectivity/WatchConnectivity.h>
并声明为 WCSessionDelegate
在应用启动后添加代码以启动会话:
if ([WCSession isSupported])
WCSession* session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
使用它来将更新的默认值发送到其他设备(在您当前的 [defaults synchronize]
之后调用):
[[WCSession defaultSession] updateApplicationContext:[[[NSUserDefaults alloc] initWithSuiteName:@"group.com.company.myapp"] dictionaryRepresentation] error:nil];
接收设置并将其保存回默认值 - 将其添加到 WCDelegate:
-(void)session:(WCSession *)session didReceiveApplicationContext:(NSDictionary<NSString *,id> *)applicationContext
NSLog(@"New Session Context: %@", applicationContext);
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.company.myapp"];
for (NSString *key in applicationContext.allKeys)
[defaults setObject:[applicationContext objectForKey:key] forKey:key];
[defaults synchronize];
注意保持对非 WC 设备的支持 - 使用 if ([WCSession isSupported])
包装您的 updateApplicationContext 调用
【讨论】:
如果您在手表应用程序未运行时更改 iPhone 应用程序的设置会怎样(isReachable 为 false)?手表应用不会收听会话。稍后,当手表应用程序启动时,它将具有旧值。手表应用需要唤醒 iPhone 应用,然后安排一些魔法发生。 优秀的答案。请注意,applicationContext 包含很多与 Apple 相关的键。为了更安全,我建议只为您期望的已验证的键名子集(例如 NSArray)添加数据。 最佳简洁答案。【参考方案4】:如前所述,共享的 NSUserDefaults 不再适用于 WatchOS2。
这是@RichAble 答案的快速版本,还有一些注释。
在您的 iPhone 应用中,按照以下步骤操作:
选择您要从中将数据推送到 Apple Watch 的视图控制器,并在顶部添加框架。
import WatchConnectivity
现在,与手表建立 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)
请注意,如果您跳过设置代理,这将不起作用,因此即使您从不使用它,您也必须设置它并添加此扩展:
extension MyViewController: WCSessionDelegate
现在,在您的手表应用中(此确切代码也适用于 Glances 和其他手表套件应用类型)您添加框架:
import WatchConnectivity
然后你设置连接会话:
override func awakeWithContext(context: AnyObject?)
super.awakeWithContext(context)
let watchSession = WCSession.defaultSession()
watchSession.delegate = self
watchSession.activateSession()
您只需收听和处理来自 iOS 应用程序的消息:
extension InterfaceController: WCSessionDelegate
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject])
print("\(applicationContext)")
dispatch_async(dispatch_get_main_queue(),
//update UI here
)
仅此而已。
注意事项:
-
您可以随时发送新的 applicationContext 并且它
手表是否在附近并已连接或手表是否
应用程序正在运行。这会在后台以
智能的方式,数据就在那里等待
手表应用已启动。
如果您的手表应用程序实际上处于活动状态并正在运行,它应该会收到
大多数情况下会立即发送消息。
您可以反转此代码,让手表将消息发送到
iPhone 应用程序也是如此。
您的手表应用在查看时收到的applicationContext 仅是您发送的最后一条消息。如果您在查看手表应用之前发送了 20 条消息,它将忽略前 19 条并处理第 20 条。
如需在 2 个应用程序之间建立直接/硬连接或后台文件传输或排队消息,请查看WWDC video。
【讨论】:
这是一个很好的例子,我让它在新的 watchOS 3 和 swift 3 等上工作。只是一个简短的说明,你必须改变你发送的值 - 而不仅仅是“bar” ,否则您的消息将只发送一次。我花了一个小时才弄清楚:(【参考方案5】:我花了好几个小时才得到这个。观看这个非常有用的视频!它为您提供了如何使用 WatchConnectivity 在 iPhone 应用程序和 wacth 之间共享 NSUserDefault 的基本概念!
https://www.youtube.com/watch?v=VblFPEomUtQ
【讨论】:
以上是关于NSUserDefaults 不适用于 Watch OS2 的 Xcode beta的主要内容,如果未能解决你的问题,请参考以下文章
Apple Watch、WatchKit 和 NSUserDefaults [重复]
带有标志 --legacy-watch 的 Nodemon 不适用于 docker Ubuntu/Linux