sendMessageData does not call didReceiveMessageData on the counterpart

I would need to return an array of dictionaries when polling the main app from the WatchKit extension. Yet it seems the only plain version of it just uses Dictionaries. So I tried packing the array in an Archive and send it instead through:

public func sendMessageData(data: NSData, replyHandler: ((NSData) -> Void)?, errorHandler: ((NSError) -> Void)?)

and receiving it by:

func session(session: WCSession,     didReceiveMessageData messageData: NSData,     replyHandler: (NSData) -> Void)

Yet, differently from the dictionary version, the latter function gets not called. What might be wrong? The version at: https://developer.apple.com/library/watchos/documentation/WatchConnectivity/Reference/WCSessionDelegate_protocol/index.html#//apple_ref/occ/intfm/WCSessionDelegate/session:didReceiveMessageData:replyHandler: reports a different format that does not compile, though. Those are the two functions:


   // watckit extension
     func poll() {
        if WCSession.defaultSession().reachable{
            let requestValue = ["command": "buspoll"]
            let data = NSKeyedArchiver.archivedDataWithRootObject(requestValue)
            if let safeSession = session{
                if safeSession.reachable {
                    safeSession.sendMessageData(data, replyHandler: {[unowned self] (resultData) -> Void in
                        let reply = NSKeyedUnarchiver.unarchiveObjectWithData(resultData) as! [Dictionary<String, String>]
                        self.myTable.setNumberOfRows(reply.count, withRowType: "WKBusRow")
                        for index in 0..<reply.count {
                            let row=self.myTable.rowControllerAtIndex(index) as! WKBusArrivalRow
                            let busDestination = reply[index]["destination"]
                            let timeLocation = reply[index]["time"]
                            row.bus.setText(busDestination)
                            print("destination=\(busDestination)")
                            row.location.setText(timeLocation)
                            print("time=\(timeLocation)")
                        }
                        }, errorHandler: { (error) -> Void in
                            print("error: \(error)")
                    })
                }
            }
        }
    }
//MainApp
    public func session(session: WCSession,
        didReceiveMessageData messageData: NSData,
        replyHandler: (NSData) -> Void) {
        var data=NSData()
        let message = NSKeyedUnarchiver.unarchiveObjectWithData(messageData) as! Dictionary<String, String>
        var replyValues = [Dictionary<String, String>]()
        print("ricevuto " + message["command"]!)
        switch message["command"]!{
            case "buspoll":
            for index in 0..<busData.count {
                replyValues[index]["destination"] = String(format: "%@: %@", busData[index], destionationsData[index])
                replyValues[index]["time"] = String(format: "%@ min. at %@", minutesData[index], locationData[index])
            }
            data = NSKeyedArchiver.archivedDataWithRootObject(replyValues)
        default:
            break
        }
        replyHandler(data)
    }

You can simply add your array of dictionaries to another dictionary and send it via

func sendMessage(_ message: [String : AnyObject],
    replyHandler replyHandler: (([String : AnyObject]) -> Void)?,
    errorHandler errorHandler: ((NSError) -> Void)?)

That is a nice idea I shall of course test, yet I am still in the black why the NSData version does not work.

Unfortunately not even this works, I suspect there might be some problems in the objective_c app delegate method:

#ifdef __IPHONE_9_0
-(void) session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *,id> *)message replyHandler:(void (^)(NSDictionary<NSString *,id> * _Nonnull))replyHandler {
    [[WatchkitCommunicator sharedInstance] session:session didReceiveMessage:message replyHandler:replyHandler];
}
#endif

Yet I do not see anything funny, of course the class adopts the WCSessionDelegate protocol.

That is how the two functions came out following your advice:

    func poll() {
        if WCSession.defaultSession().reachable{
            let requestValue = ["command": "buspoll"]
            let session=WCSession.defaultSession()
            session.sendMessage(requestValue, replyHandler: {[unowned self] (mainDictionary) -> Void in
                let reply=mainDictionary["response"] as! Dictionary<String, AnyObject>
                self.myTable.setNumberOfRows(reply.count, withRowType: "WKBusRow")
                for index in 0..<reply.count {
                    print("count=\(reply.count)")
                    let row=self.myTable.rowControllerAtIndex(index) as! WKBusArrivalRow
                    let busDestination = requestValue["destination"]
                    let timeLocation = requestValue["time"]
                    row.bus.setText(busDestination)
                    print("destination=\(busDestination)")
                    row.location.setText(timeLocation)
                    print("time=\(timeLocation)")
                }
                }, errorHandler: { (error) -> Void in
                    print("error: \(error)")
            })
        }
    }
and
    public func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void){
        var replyValues = [Dictionary<String, AnyObject>()]
        switch message["command"] as! String{
        case "buspoll":
            for index in 0..<busData.count {
                replyValues[index]["destination"] = String(format: "%@: %@", busData[index], destionationsData[index])
                replyValues[index]["time"] = String(format: "%@ min. at %@", minutesData[index], locationData[index])
            }
        default:
            break
        }
        let containingDictionary=Dictionary(dictionaryLiteral: ("response", replyValues))
        replyHandler(containingDictionary)
    }

As a matter of fact I even put a local notification in the appDelegate to see if the control passed through there, but it was not delivered. SO I suspect the problem is in the objective-c code, but I ignore what it might be. At present I would not be so willing to translate the full apps delegate to Swift, if not absolutely necessary.

Why are you using "the objective_c app delegate method"?

Here is an example in Swift:

import UIKit
import WatchConnectivity

class ConnectivityHelper: NSObject, WCSessionDelegate {
    var session: WCSession!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        if (WCSession.isSupported()) {
            session = WCSession.defaultSession()
            session.delegate = self;
            session.activateSession()
        }
    }

    func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
        let value = message["Message"] as? String
        dispatch_async(dispatch_get_main_queue()) {
            // do something
        }

        replyHandler(["Message":"Oh yeah!"])
    }
}


And in Objective-C:


ConnectivityHelper.h

#import <Foundation/Foundation.h>
#import <WatchConnectivity/WatchConnectivity.h>

@interface ConnectivityHelper : NSObject <WCSessionDelegate>

@end


ConnectivityHelper.m

#import "ConnectivityHelper.h"

@implementation ConnectivityHelper

- (id) init {

    if (self = [super init]) {

        if ([WCSession isSupported]) {
            WCSession *session = [WCSession defaultSession];
            session.delegate = self;
            [session activateSession];
        }
    }

    return self;
}

- (void)session:(nonnull WCSession *)session didReceiveMessage:(nonnull NSDictionary<NSString *,id> *)message replyHandler:(nonnull void (^)(NSDictionary<NSString *,id> * _Nonnull))replyHandler {
     dispatch_async(dispatch_get_main_queue()) {
          // do something
     }

     NSDictionary *replyDict = [NSDictionary dictionaryWithObjectsAndKeys:@"Oh yeah!", @"Message", nil];
     replyHandler(replyDict);
}

@end


Just load ConnectivityHelper in your AppDelegate.

As a matter of fact I ported the appDelegate to Swift, it took half a day but it was worthwhile. Now the callback is regularly called but I have a new problem in returning a described in the appropriate ticket.

Perhaps the issue was due to the fact I opened the session on the helper class instead of on the delegate ad that prevented the calling of the delegate function.

Accepted Answer

The issue was due to the fact I opened the WCSession on the helper class instead of the AppDelegate, and that prevented the didReceiveMessageData from being triggered.

sendMessageData does not call didReceiveMessageData on the counterpart
 
 
Q