NSUserDefaults 无法在 Watch OS2 的 Xcode 测试版上运行

NSUserDefaults not working on Xcode beta with Watch OS2

我刚刚安装了 Xcode 的最新测试版来尝试 Swift 2 以及对 Apple Watch 开发部分所做的改进。

我真的很难弄清楚为什么要用这种基本的 NSUserDefaults 方法在 iOSWatch 之间共享信息OS2 不工作。

我跟着 this step-by-step tutorial 检查我是否遗漏了过程中的某些东西,比如为 phone 应用程序和扩展程序打开同一个组,但这是我得到的:没有.

这是我在 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 似乎利用了不同的组...他们不共享信息,他们将它们存储在本地。

有人遇到同样的问题吗?知道如何解决吗?

在 watch OS2 中,您不能再使用共享组容器。 Apple Docs:

Watch apps that shared data with their iOS apps using a shared group container must be redesigned to handle data differently. In watchOS 2, each process must manage its own copy of any shared data in the local container directory. For data that is actually shared and updated by both apps, this requires using the Watch Connectivity framework to move that data between them.

NSUserDefaults(即使有一个应用程序组)不会在 iPhone 和 watchOS 2 中的手表之间同步。如果你想从你的 iPhone 应用程序或设置同步设置- Watch.bundle,您必须自己处理同步。

我发现在这种情况下使用 WatchConnectivity 的用户信息传输非常有效。 您将在下面找到有关如何实现此功能的示例。该代码仅处理从 phone 到 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) 确定何时需要将设置推送到手表
(此处未显示)

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];  
}  

有一种简单的方法可以重现旧功能,我将旧组用户默认值导出到字典中,将其发送到 WatchConnectivity 框架,然后将它们重新导入到另一端的用户默认值中:

在 Phone 和 Watch 应用程序中:

  1. 添加 WatchConnectivty 框架
  2. #import <WatchConnectivity/WatchConnectivity.h> 并声明为 WCSessionDelegate
  3. 添加代码以在应用程序启动后启动会话:

    if ([WCSession isSupported]) {
            WCSession* session = [WCSession defaultSession];
            session.delegate = self;
            [session activateSession];
        }
    
  4. 使用它来将更新的默认设置发送到其他设备(在您当前的 [defaults synchronize] 之后调用):

[[WCSession defaultSession] updateApplicationContext:[[[NSUserDefaults alloc] initWithSuiteName:@"group.com.company.myapp"] dictionaryRepresentation] error:nil];

  1. 接收并将设置保存回默认值 - 将其添加到 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 调用

我花了好几个小时才弄到这个。观看这个非常有用的视频!它为您提供了如何使用 WatchConnectivity 在 iPhone 应用程序和 wacth!

之间共享 NSUserDefault 的基本概念

https://www.youtube.com/watch?v=VblFPEomUtQ

如前所述,共享 NSUserDefaults 不再适用于 WatchOS2。

这是@RichAble 回答的 swift 版本,还有一些注释。

在您的 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
        })
    }

}

仅此而已。

注意事项:

  1. 您可以随时发送新的 applicationContext,并且它 不管手表是否在附近并已连接或手表是否 应用是 运行。这会在后台提供数据 智能方式,数据就在那里等待 手表应用程序已启动。
  2. 如果您的手表应用确实处于活动状态并且 运行,它应该会收到 大多数情况下会立即显示消息。
  3. 您可以反转此代码让手表向 iPhone应用同理。
  4. 您的手表应用程序在被查看时收到的 applicationContext 只会是您发送的最后一条消息。如果您在查看手表应用程序之前发送了 20 条消息,它将忽略前 19 条并处理第 20 条。
  5. 要在 2 个应用程序之间建立 direct/hard 连接或进行后台文件传输或排队消息传递,请查看 WWDC video。