Watch OS 2 and App Groups.

Do App Groups still work on Watch OS 2 as a mechanism for sharing files between the watch and iPhone?

(Since WatchKit extension will now reside on the watch instead of the iPhone)

What changes to we need to make to the existing codebase?

Answered by douglasak in 7818022

See the documentation on Watch Connectivity framework. App Groups are no longer applicable.


"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."


https://developer.apple.com/library/prerelease/watchos/documentation/WatchConnectivity/Reference/WatchConnectivity_framework/index.html#//apple_ref/doc/uid/TP40015269

I think not, this is from the transition guide:


"Your extension now stores files and data on Apple Watch. Any data that is not part of your Watch app or WatchKit extension bundle must be fetched the network or from the companion iOS app running on the user’s iPhone. You cannot rely on a shared group container to exchange files with your iOS app. Fetching files involves transferring them wirelessly to Apple Watch."


This is a little troublesome for those of us currently using Core Data in a shared group. Is there any stremlined way to keep a core database in sync across the parent app and extension?

Accepted Answer

See the documentation on Watch Connectivity framework. App Groups are no longer applicable.


"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."


https://developer.apple.com/library/prerelease/watchos/documentation/WatchConnectivity/Reference/WatchConnectivity_framework/index.html#//apple_ref/doc/uid/TP40015269

Would this also apply for shared NSUserDefaults?

Yes, that's my understanding.

Ugh, not good. I'll have to update two apps.

Here's sample code that worked for me (excluding the delegate methods) although I am having some issues with the 'reachable' property of WCSession. In the simulator, 'reachable' is returning as true even when the Watch app is not in the foreground.


    override func viewDidLoad() {
        super.viewDidLoad()

        if(WCSession.isSupported()) {
            WCSession.defaultSession().delegate = self
            WCSession.defaultSession().activateSession()
        }
    }
  
    @IBAction func updateLabel(sender: AnyObject) {
       
        numberValue = viewSlider.value
       
        viewLabel.text = numberValue.description
       
        if(WCSession.defaultSession().reachable) {
            WCSession.defaultSession().sendMessage(["msgContents" : numberValue], replyHandler: nil, errorHandler: errorHandler)
        }
       
        else if(WCSession.isSupported()) {
            do {
                try WCSession.defaultSession().updateApplicationContext(["msgContents" : numberValue])
            }
               
            catch let error as NSError {
                errorHandler(error)
            }
        }
    }

    func errorHandler (error: NSError) -> Void {
       
        if let errorMessage = returnWatchError(error.code) {
            NSLog("Error: \(errorMessage)")
        }
       
    }
   
   
    func returnWatchError (errorCode: Int) -> String? {
       
        var errorString: String?
       
        if let errorDescription = WCErrorCode(rawValue: errorCode) {
           
            switch errorDescription {
            case .GenericError:
                errorString = "GenericError"
            case .SessionNotSupported:
                errorString = "SessionNotSupported"
            case .SessionMissingDelegate:
                errorString = "SessionMissingDelegate"
            case .SessionNotActivated:
                errorString = "SessionNotActivated"
            case .DeviceNotPaired:
                errorString = "DeviceNotPaired"
            case .WatchAppNotInstalled:
                errorString = "WatchAppNotInstalled"
            case .NotReachable:
                errorString = "NotReachable"
            case .InvalidParameter:
                errorString = "InvalidParameter"
            case .PayloadTooLarge:
                errorString = "PayloadTooLarge"
            case .PayloadUnsupportedTypes:
                errorString = "PayloadUnsupportedTypes"
            case .MessageReplyFailed:
                errorString = "MessageReplyFailed"
            case .MessageReplyTimedOut:
                errorString = "MessageReplyTimedOut"
            case .FileAccessDenied:
                errorString = "FileAccessDenied"
               
            }
        }
       
        return errorString
    }

I tried sharing data by NSUserDefaults(suiteName:) on iOS9/watchOS2 Simulator without Watch Connectivity framework.

And it worked.


Does the simulator have a bug? Or the framework is not required for shared NSUserDefaults.

How awful. The way most existing apps shared data now doesn't work.

Yeah, as I mentioned in another thread, there's going to be a huge public outcry when users discover their favorite apps no longer work in 2.0. Apple needs to make a 1.0 compatibility mode!

watchOS 1 apps, where the extension is running on the paired iPhone, are compatable with watchOS 2. These apps will continue to function and have access to a shared app group between the WatchKit extension and parent application because the extension and app are running on the same device.


If you're experiencing different behavior, please file a bug report at bugreport.apple.com.

Can you give any hint about how you made it work. I'm going insane over this issue.

Does this include NSUserDefaults?


On the iPhone FirstViewController.m and the InterfaceController.m I have:

    NSUserDefaults *prefs = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.webcars.counter2"];

    [prefs setInteger:myInt forKey:@"countKey"];

    NSInteger myInt = [prefs integerForKey:@"countKey"];


Before Watch OS 2 They shared the same data just fine. After Watch OS 2, that no longer happens; myInt on the iPhone app and the Watch are independent.


I'm missing something, but what?

On watchOS2 you can still use NSUserDefaults with shared groups but only for reading. Settings-Watch.bundle for example.

This new way of communicating is completely and utterly unreliable. it NEVER gets the data from the phone when the app is not running. SharedDefaults data should always be available.


I HATE what apple has done here.

I can't find a way to make it work, even for read-only, on watchOS 3...

Watch OS 2 and App Groups.
 
 
Q