Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSUserDefaults not working on Xcode beta with Watch OS2

Tags:

I just installed the latest beta of Xcode to try Swift 2 and the improvements made to the Apple Watch development section.

I'm actually having an hard time figuring out WHY this basic NSUserDefaults method to share informations between iOS and Watch OS2 isn't working.

I followed this step-by-step tutorial to check if I missed something in the process, like turning on the same group for both the phone application and the extension, but here's what I got: NOTHING.

Here's what I wrote for the ViewController in the iPhone app:

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()     } } 

And here's what I have in the InterfaceController for WatchKit:

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()     } } 

I made some tests and it seems like the iOS app and the Watch OS one take advantage of different groups...they're not sharing information, they store them locally.

Is someone having the same issue? Any idea how to fix it?

like image 640
Sara Canducci Avatar asked Jun 15 '15 17:06

Sara Canducci


Video Answer


2 Answers

With watch OS2 you can no longer use shared group containers. 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.

like image 178
rmp Avatar answered Sep 17 '22 15:09

rmp


NSUserDefaults (even with an App Groups) don't sync between the iPhone and the Watch in watchOS 2. If you want to sync settings from either your iPhone app or the Settings-Watch.bundle, you have to handle the syncing yourself.

I've found that using WatchConnectivity's user info transfers works really well in this case. Below you'll find an example of how you could implement this. The code only handles one-way syncing from the phone to the Watch, but the other way works the same.

In the iPhone app:
1) Prepare dictionary of settings that need to be synced

- (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) Determine when the settings need to be pushed to the Watch
(not shown here)

3) Push the settings to the 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];        }   }   

In the Watch extension:
4) Receive the user info transfer

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

5) Save the received settings to the user defaults on the 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];   }   
like image 41
Fabian Kreiser Avatar answered Sep 19 '22 15:09

Fabian Kreiser