NSUserDefaults没有使用Watch OS2开发Xcode beta

Sar*_*cci 23 operating-system nsuserdefaults swift watchkit watchos-2

我刚刚安装了最新的Xcode测试版来试用Swift 2以及Apple Watch开发部分的改进.

我实际上很难搞清楚为什么这种NSUserDefaultsiOSWatch OS2之间共享信息的基本方法无法正常工作.

我按照这个循序渐进的教程来检查我是否错过了这个过程中的某些内容,比如打开手机应用程序和扩展程序的同一组,但这就是我得到的:没有.

这是我在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()
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是我在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()
    }
}
Run Code Online (Sandbox Code Playgroud)

我做了一些测试,似乎iOS应用程序和Watch OS可以利用不同的组... 他们不共享信息,他们将它们存储在本地.

有人有同样的问题吗?知道怎么解决吗?

rmp*_*rmp 42

使用手表OS2,您无法再使用共享组容器. Apple Docs:

观看使用共享组容器与其iOS应用共享数据的应用必须重新设计,以便以不同方式处理数据.在watchOS 2中,每个进程必须管理自己的本地容器目录中任何共享数据的副本.对于两个应用实际共享和更新的数据,这需要使用Watch Connectivity框架在它们之间移动数据.

  • 天哪 - 过去几天浪费时间了. (16认同)
  • 我希望我早点找到这个......谢谢 (3认同)
  • 谢谢你救了我的命. (2认同)
  • DOH! - 今晚我想"嘿,我为什么不重新设计我的应用程序以使用共享应用程序组"然后我花了几个小时试图找出它为什么不起作用.最糟糕的部分?两周前,我看到并接受了这个答案! (2认同)

Fab*_*ser 18

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];  
}  
Run Code Online (Sandbox Code Playgroud)

2)确定何时需要将设置推送到Watch
(此处未显示)

3)将设置推送到Watch

- (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];  
     }  
}  
Run Code Online (Sandbox Code Playgroud)

Watch扩展中:
4)接收用户信息传输

- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo  
{  
    NSDictionary *settings = [userInfo objectForKey:@"settings"];  
    if (settings != nil) {  
        // Import the settings  
        [self importSettingsFromCompanionApp:settings];  
     }  
} 
Run Code Online (Sandbox Code Playgroud)

5)将收到的设置保存到Watch上的用户默认值

- (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];  
}  
Run Code Online (Sandbox Code Playgroud)


A.B*_*ger 12

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

在手机和手表应用中:

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

    if ([WCSession isSupported]) {
            WCSession* session = [WCSession defaultSession];
            session.delegate = self;
            [session activateSession];
        }
    
    Run Code Online (Sandbox Code Playgroud)
  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];
    }
    
    Run Code Online (Sandbox Code Playgroud)

小心保持对非WC设备的支持 - 用你的updateApplicationContext调用包装 if ([WCSession isSupported])


Wil*_* T. 7

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

这是@ RichAble的答案的快速版本,还有一些注释.

在您的iPhone App中,请按照下列步骤操作:

选择要将数据推送到Apple Watch的视图控制器,并在顶部添加框架.

import WatchConnectivity
Run Code Online (Sandbox Code Playgroud)

现在,与watch建立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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果您跳过设置委托,这将不起作用,所以即使您从未使用它,您必须设置它并添加此扩展名:

extension MyViewController: WCSessionDelegate {

}
Run Code Online (Sandbox Code Playgroud)

现在,在您的监视应用程序中(此精确代码也适用于Glances和其他监视工具包应用程序类型),您可以添加框架:

import WatchConnectivity
Run Code Online (Sandbox Code Playgroud)

然后设置连接会话:

override func awakeWithContext(context: AnyObject?) {
    super.awakeWithContext(context)
    let watchSession = WCSession.defaultSession()
    watchSession.delegate = self
    watchSession.activateSession()
}
Run Code Online (Sandbox Code Playgroud)

你只需要听取并处理来自iOS应用程序的消息:

extension InterfaceController: WCSessionDelegate {

    func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
        print("\(applicationContext)")
        dispatch_async(dispatch_get_main_queue(), {
            //update UI here
        })
    }

}
Run Code Online (Sandbox Code Playgroud)

这里的所有都是它的.

注意事项:

  1. 您可以根据需要随时发送新的applicationContext,如果手表在附近并且已连接或手表应用程序正在运行,则无关紧要.这将以智能方式在后台传送数据,并且数据在手表应用程序启动时等待.
  2. 如果您的监视应用实际上处于活动状态且正在运行,则在大多数情况下它应立即收到该消息
  3. 您可以反转此代码,让手表以相同的方式向iPhone应用程序发送消息.
  4. 您的监视应用在查看时收到的applicationContext将只是您发送的最后一条消息.如果您在查看监视应用程序之前发送了20条消息,它将忽略前19条消息并处理第20条消息.
  5. 要在2个应用程序之间进行直接/硬连接或进行后台文件传输或排队消息传递,请查看WWDC视频 o.