WCSession transferFile and transferUserInfo problem

The two background delivery methods (transferFile, transferUserInfo) usually stop working on watchOS 2.2 and only a watch restart helps to get them work again. After restarting, they work for a while the way they always should, then stop working again... Other methods (sendMessage, sendMessageData) are ok.

There was not any problems like this on watchOS 2.1.


Has anyone experienced this? Any workoaround?

Replies

You'll need to add code to use whichever interface is presently available, or only support one (hopefully the newer one, in this case.) Kind of odd that Apple broke this in a point release, as someone else said.


I wonder if it actually fixes other intermittent issues in transferUserInfo, by requiring you to activate the session if it is not.

Having exactly the same issue. After watchos 2.2 update tranferUserInfo and transferFile stopped working... Now I have tons of outstanding file transfers but they are not being delivered.

I'm also having this problem with ios9.3/watchos2.2 and I’ve been getting emails from users about it. 😟. In my case I'm using the updateApplicationContext when the Session is not reachable, but these don't always get received now (on the watch) when the watch app returns to the foreground as they did before.


I've tried implementing the new methods but these made no difference.


In our app this causes a huge problem since the phone and watch becomes out of sync, and the Apple Watch keeps showing a workout session that has ended and there's no way to stop it.

I'm also seeing a variation of this problem with transferCurrentComplicationUserInfo. Submitted a radar which was closed and marked as a duplicate of another open issue (which I obviously can't see).


Fingers crossed we get a point release soon. Basically rendered my watch app useless.

Thought this would be fixed in todays beta 1 of watchOS 2.2.1 and iOS 9.3.2 but unfortunately issue is still present. Only way to get the transfer start working again for me is to restart the devices.

Hello all. If you look in the documentation for both the watchOS and iOS side of WatchConnectivity (there are differences!), you will notice that all of the `send...` and `transfer...` methods require you to check for the activation state of the session. The activation state can change at any time, so you need to check right before you call one of those methods.


WCSession in watchOS:

https://developer.apple.com/library/watchos/documentation/WatchConnectivity/Reference/WCSession_class/index.html#//apple_ref/occ/cl/WCSession

WCSession in iOS:

https://developer.apple.com/library/ios/documentation/WatchConnectivity/Reference/WCSession_class/index.html#//apple_ref/occ/cl/WCSession


I have noticed that one fail-over pattern when using the `sendMessage:replyHandler:errorHandler:` and `sendMessageData:replyHandler:errorHandler:` methods, is to call one of the `transfer...` methods in the errorHandler. This is the wrong approach. The same condition that caused the `sendMessage...` method to fail may cause the `transfer...` method to fail also.


If your messages from the companion iPhone to the watch are not being delivered, a better fallback would be to wait until the user activated the watch app, and then use WatchConnectivity to ask the iPhone for any un-synced data. Normally you want to keep the "truth" data on the iPhone anyway. I do realize that there are many use cases, and this will not work for all of them.


Once you have double checked the documentation and are sure you are using the APIs correctly and if you are still experiencing issues with WatchConnectivity, we would very much appreciate your bug reports, which you can file at https://developer.apple.com/bug-reporting/.

I have noticed that one fail-over pattern when using the `sendMessage:replyHandler:errorHandler:` and `sendMessageData:replyHandler:errorHandler:` methods, is to call one of the `transfer...` methods in the errorHandler. This is the wrong approach. The same condition that caused the `sendMessage...` method to fail may cause the `transfer...` method to fail also.


Sure, but in the recent past (2.0), this was the only way to get somewhat reliable communication via WatchConnectivity because the act of switching from a glance to the app would deactivate the session and would not allow you to reactivate it, so sendMessage would forever fail thereafter, but transferUserInfo would keep working.


Obviously, if the framework behaved as it was documented, none of that would be necessary.


WatchConnectivity is easily the buggiest, most unreliable piece of Apple code I've had the misfortune of using in the 15+ years I've been developing for Apple platforms. It has broken my particular application in so many ways that leave me with no answers for customers except "You'll have to restart your watch and iPhone" and "We're waiting for Apple to fix it" and it has unfortunately hurt our app store ratings and hurt our business. Were it up to me, we'd withdraw the watch extension from the app completely (but it's not up to me).


And the most discouraging part of it all is that WatchConnectivity has gotten worse with subsequent releases, not better.


Please pardon the rant. The amount of heartburn this has caused me personally over the last 6 months is immeasurable.

If anyone is still seeing problems with beta 1 of watchOS 2.2.1 and iOS 9.3.2, please file a bug as soon as possible and/or update your current bug with this piece of information. There were some good fixes that went in to these releases, but it'd be very helpful to know what remaining issues you are still encountering.

I don't know if you reviewed the release notes, but there is no indication that any WatchConnectivity fixes were made. Maybe you could ask the release team to update the watchOS and iOS beta release notes? I can understand the frustration, as the API changed in 2.2, which seems to break behavior with people using transferUserInfo in the old documented way, and now there is a new beta with no indication that anything has changed in this area.


I know when I reviewed the release notes I didn't see any reason to test that beta, as it indicated no changes, making me wonder why a 2.2.1 was posted at all.

Does watchOS2.2.1 and iOS 9.3.2 fix the issues with watch settings bundles?

Yes, the communication bugs are still present on watchOS 2.2.1 / iOS 9.3 and we are also receiving reports from our users that sendMessage: won't always work from watch to device eventhough everything is in order (device is reachable and activated and all that). This behaviour seems to also be completely random and cannot easily be reproduced.


I hope that there is some common root cause for all these unfortunate issues that are causing problems with WatchConnectivity and that they would get quickly fixed!

Is that a typo? Did you mean beta iOS 9.3.2, or the released 9.3 or 9.3.1?

It's a typo, meant beta iOS 9.3.2.

Dear Apple,


The main issue is that delegate method 'session:didReceiveFile:' on the Watch part is only called during the first session after rebooting the Watch.

It's never called again, even though 'session:didFinishFileTransfer:' is called without error on the iOS part, after using 'transferFile:'.

Our team is wondering whether we have implemented the new delegate methods incorrectly, or that there are more bugs.

We are using iOS 9.3.2 beta 1 and watchOS 2.2.1 beta 1.

Can you explain in detail what the 3 new mandatory delegate methods in watchOS 2.2 and iOS 9.3 do, and moreover, suggest what code should be used inside?

- (void)session:(WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(nullable NSError *)error
- (void)sessionDidBecomeInactive:(WCSession *)session
- (void)sessionDidDeactivate:(WCSession *)session


Our current implementation


Before calling 'transferFile:' on iOS, we verify that a WCSession is activated and the watch is paired via this convenience method 'isPaired':


- (BOOL)isPaired {
    if ([WCSession class]) {
        if ([WCSession isSupported]) {
            // For watchOS 2.2 and higher it's mandatory to check the value of property 'activationState':
            if ([[WCSession defaultSession] respondsToSelector:@selector(activationState)]) {
                NSLog(@"Property 'activationState' exists -> watchOS 2.2 or higher. Value = %ld", (long)[_watchSession activationState]);

                // When not activated, return NO:
                if ([_watchSession activationState] != WCSessionActivationStateActivated) {
                    return NO;
                }
            }

            BOOL paired = _watchSession.paired;
            CLS_LOG(@"AppDelegate: isPaired = %d", paired);

            return paired;
        }
    }

    return NO;
}


So 'transferFile:' will never be called when "activationState" is not WCSessionActivationStateActivated, according to your documentation.


Currently we only use 'activationDidCompleteWithState:' on the Watch part, since the other 2 methods are not part of WCSessionDelegate for watchOS (only on iOS).

It's used in singleton class ConnectivityManager, to start sending message data to the paired iPhone via WCSession 'sendMessage:replyHandler:errorHandler'. Method 'activationDidCompleteWithState:' is called async after calling 'activateSession' in the init of the singleton. This works flawless.


- (id)init {
    if (self = [super init]) {
        NSLog(@"ConnectivityManager: init");

          // Activate Connectivity session with iPhone:
        WCSession *session = [WCSession defaultSession];
        session.delegate = self;
        [session activateSession];
     }

     return self;
}

- (void)session:(WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(nullable NSError *)error{
    NSLog(@"ConnectivityManager: activationDidCompleteWithState: %d", activationState);

    if (activationState == WCSessionActivationStateActivated) {
         // Connect to phone:
         [self connectToPhone];
        }
    }
}


Our iOS app acts upon requests from the Watch via 'session:didReceiveMessage:replyHandler:'. Any action on the iOS app that follows on a request is preceded by the above described method 'isPaired' to ensure that "activationState" equals WCSessionActivationStateActivated.

So basically we have implemented the 3 new delegate methodswithout functionality, except 'sessionDidDeactivate'. That's populated according to a previous response in this topic:

- (void)session:(WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(nullable NSError *)error{
    CLS_LOG(@"AppDelegate: activationDidCompleteWithState: %ld", (long)activationState);
}
- (void)sessionDidBecomeInactive:(WCSession *)session{
    CLS_LOG(@"AppDelegate: sessionDidBecomeInactive:");
}
- (void)sessionDidDeactivate:(WCSession *)session {
    CLS_LOG(@"AppDelegate: sessionDidDeactivate");
    [_watchSession activateSession];
}


Please advice how we should change the code in order to have a 100% succes rate in seeing the delegate method 'session:didReceiveFile:' on the Watch part being called after using 'transferFile:' on the iOS app.


Thanks for your support,

Martijn

I've submitted a bug report to Apple, which you can also find here: https://openradar.appspot.com/radar?id=6059348350468096


For this purpose I've also created a little app which increments a counter on both iPhone and watch by sending user info transfers to the watch. Follow the steps inside the bug report and you should notice the described issues.

You can download the project here: http://snipnotes.de/files/WCDemo.zip Feel free to modify the code and use it for your own bug reports.