WatchKit settings bundle in 2.0?

In the original WatchKit, one could set up a WatchKit settings bundle which would specify settings to be included in the app's page within the Apple Watch app.


The settings were written to a shared NSUserDefaults identified by an app group name.

The WatchKit extension would be given an App Groups entitlement to allow it to read the shared NSUserDefaults.


All this worked well in the original WatchKit. I know, because I used it.


With Watch OS 2, the WatchKit extension runs on the watch and not on the phone. App Groups are [apparently] no longer used. At least, I have tried using them and the settings do not transfer from the iPhone to the watch.


So: in Watch OS 2, how and where are the settings specified by the WatchKit settings bundle stored, and how can they be retrieved by the extension running on the watch?

Replies

Hi,


I've asked the same question some time ago: Can't get settings bundle to work

No answer so far. 😟


Dirk

I am having the same issue, has anyone figured this out?

NSUserDefaults (even with an App Groups) don't sync between the iPhone and the Watch. 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];
}

Hi,


many thanks for your detailed answer.


@Apple: Is this really the intended way to get settings for WatchApps to work? Seriously?


Dirk

Does this actually work on a real device?


I have an existing settings bundle in the main Settings App for my iPhone as well but anything that I put in my Watch Settings Bundle never shows up in NSUserDefaults. Although it does work on the simulator.

In watchOS 2 you can only read Settings-Watch.bundle.

To access app settings you should only initialize a suite name for your specific app group, just as you done in watchOS 1.

Sounds like you just read that in the docs and haven't actually tried to do it. I can confirm that it does not work even though that is what is stated in the docs.

It works for me by following the offical document.

People are saying this works but I still cannot get it to work even though it was working in watchOS 1.


1. I added the shared group to both iOS app and watch extension with the same bundle identifier

2. I added the Settings-Watch.bundle to the iOS app and added the ApplicationGroupContainerIdentifier to the Plist


I can see the options I added in the Watch app (on iPhone) but the watch doesn't read the settings. What am I missing? I don't have to do anything related to WatchConnectivity to get this thing working right? Are the people that are claiming that it works sure you are on WatchOS 2?

I doubt people still work on watchOS 1 app. Mine work on the device with watchOS2 and no WatchConnectivity involved. FYI it dosen't work on beta5 but GM built works for me.

Yes, in watchOS GM built it "just works". 🙂

I'm trying this on GM and it is not working.


Ugh. I guess I have no choice but to try to get this working on an empty project then check each setting to see which random setting will magically make it work.

I'm ready to give up and become a farmer for the rest of my life. There is no reason why this shouldn't be working.


Can this give a hint or spark some light bulb for anyone? The code below produces different output on the iOS app and the watch extension.


NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.emergingmarkets.commoditiesLite"];
NSLog(defaults.dictionaryRepresentation.description);


On iOS app the output is:

{
    AddingEmojiKeybordHandled = 1;
    AppleITunesStoreItemKinds =     (
        audiobook,
        "tv-episode",
        booklet,
        software,
        "software-update",
        "itunes-u",
        ringtone,
        "tv-season",
        movie,
        mix,
        newsstand,
        song,
        wemix,
        tone,
        artist,
        "podcast-episode",
        podcast,
        document,
        eBook,
        album,
        "music-video"
    );
    AppleKeyboards =     (
        "en_US@hw=US;sw=QWERTY",
        "emoji@sw=Emoji",
        "en_US@hw=US;sw=QWERTY"
    );
    AppleKeyboardsExpanded = 1;
    AppleLanguages =     (
        "en-US"
    );
    AppleLanguagesDidMigrate = "9.0";
    AppleLocale = "en_US";
    ApplePasscodeKeyboards =     (
        "en_US@hw=US;sw=QWERTY",
        "emoji@sw=Emoji",
        "en_US@hw=US;sw=QWERTY"
    );
    "GC*0" = 0;
    "HG*0" = 0;
    MSVLoggingMasterSwitchEnabledKey = 0;
    NSInterfaceStyle = macintosh;
    NSLanguages =     (
        "en-US",
        en
    );
    PKEnableStockholmSettings = 1;
}


On watch extension the output is:

{
    AppleKeyboards =     (
        "en_US",
        emoji,
        "en_US"
    );
    AppleKeyboardsExpanded = 1;
    AppleLanguages =     (
        en
    );
    AppleLanguagesDidMigrate = "2.0";
    AppleLocale = "en_US";
    MSVLoggingMasterSwitchEnabledKey = 0;
    NSInterfaceStyle = macintosh;
    NSLanguages =     (
        en
    );
}


The values for the keys "GC*0" and "HG*0" (seen in the iOS app output) are the only ones that come from the Settings bundle. They are the ones that are suppose to show up in the watch extension also.


Helppppppppppppppp

Do you have a

WCSession object? Are you doing anything about WatchConnectivity? Are you doing anything at all different in WatchOS 2?