Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCSession Send Message gives error "payload could not be delivered"

I am using Xamarin to develop an Apple Watch app. I am trying to send a message from my watch to the iPhone with my SendMessage function. When I do this, I get in the out error the message payload could not be delivered. I can only read part of the message ("payload could n...") because I am writing it on a label (since my debugger doesn't work in Xamarin I can't take a look at the message), but after doing some googling I'm assuming that's what it is written. Any idea why? Here is my code:

    public sealed class WCSessionManager : NSObject, IWCSessionDelegate
{
    // Setup is converted from https://www.natashatherobot.com/watchconnectivity-say-hello-to-wcsession/ 
    // with some extra bits
    private static readonly WCSessionManager sharedManager = new WCSessionManager();
    private static WCSession session = WCSession.IsSupported ? WCSession.DefaultSession : null;

#if __IOS__
    public static string Device = "Phone";
#else
    public static string Device = "Watch";
#endif

    public event ApplicationContextUpdatedHandler ApplicationContextUpdated;

    public delegate void ApplicationContextUpdatedHandler(WCSession session, Dictionary<string, object> applicationContext);

    public event MessageReceivedHandler MessageReceived;

    public delegate void MessageReceivedHandler(Dictionary<string, object> message, Action<Dictionary<string, object>> replyHandler);

    private WCSession validSession
    {
        get
        {
#if __IOS__
            // Even though session.Paired and session.WatchAppInstalled are underlined, it will still build as they are available on the iOS version of WatchConnectivity.WCSession
            Console.WriteLine($"Paired status:{(session.Paired ? '✓' : '✗')}\n");
            Console.WriteLine($"Watch App Installed status:{(session.WatchAppInstalled ? '✓' : '✗')}\n");
            return (session.Paired && session.WatchAppInstalled) ? session : null;
            //return session;
#else
            return session;
#endif
        }
    }

    private WCSession validReachableSession
    {
        get
        {
            return session.Reachable ? validSession : null;
        }
    }

    private WCSessionManager() : base() { }

    public static WCSessionManager SharedManager
    {
        get
        {
            return sharedManager;
        }
    }

    public void StartSession()
    {
        if (session != null)
        {
            session.Delegate = this;
            session.ActivateSession();
            Console.WriteLine($"Started Watch Connectivity Session on {Device}");
        }
    }

    [Export("sessionReachabilityDidChange:")]
    public void SessionReachabilityDidChange(WCSession session)
    {
        Console.WriteLine($"Watch connectivity Reachable:{(session.Reachable ? '✓' : '✗')} from {Device}");
        // handle session reachability change
        if (session.Reachable)
        {
            // great! continue on with Interactive Messaging
        }
        else {
            // 😥 prompt the user to unlock their iOS device
        }
    }

    #region Application Context Methods

    public void UpdateApplicationContext(Dictionary<string, object> applicationContext)
    {
        // Application context doesnt need the watch to be reachable, it will be received when opened
        if (validSession != null)
        {
            try
            {
                var NSValues = applicationContext.Values.Select(x => new NSString(JsonConvert.SerializeObject(x))).ToArray();
                var NSKeys = applicationContext.Keys.Select(x => new NSString(x)).ToArray();
                var NSApplicationContext = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(NSValues, NSKeys);

                UpdateApplicationContextOnSession(NSApplicationContext);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception Updating Application Context: {ex.Message}");
            }
        }
    }

    public void GetApplicationContext()
    {
        UpdateApplicationContext(new Dictionary<string, object>() { { "GET", null } });
    }

    [Export("session:didReceiveApplicationContext:")]
    public void DidReceiveApplicationContext(WCSession session, NSDictionary<NSString, NSObject> applicationContext)
    {
        Console.WriteLine($"Recieving Message on {Device}");
        if (ApplicationContextUpdated != null)
        {
            var keys = applicationContext.Keys.Select(k => k.ToString()).ToArray();

            IEnumerable<object> values;

            try
            {
                values = applicationContext.Values.Select(v => JsonConvert.DeserializeObject(v.ToString(), typeof(DoorWatchDTO)));
            }
            catch (Exception)
            {
                values = applicationContext.Values.Select(v => JsonConvert.DeserializeObject(v.ToString()));
            }

            var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
                                 .ToDictionary(x => x.Key, x => x.Value);

            ApplicationContextUpdated(session, dictionary);
        }
    }

    [Export("session:didReceiveMessage::")]
    public void DidReceiveMessage(WCSession session, NSDictionary<NSString, NSObject> message, WCSessionReplyHandler replyHandler)
    {
        if (MessageReceived != null)
        {
            var keys = message.Keys.Select(k => k.ToString()).ToArray();

            IEnumerable<object> values;

            values = message.Values.Select(v => JsonConvert.DeserializeObject(v.ToString()));

            var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
                                 .ToDictionary(x => x.Key, x => x.Value);

            MessageReceived(dictionary, (dict) =>
            {
                var NSValues = dict.Values.Select(x => new NSString(JsonConvert.SerializeObject(x))).ToArray();
                var NSKeys = dict.Keys.Select(x => new NSString(x)).ToArray();
                var NSDict = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(NSValues, NSKeys);

                replyHandler.Invoke(NSDict);
            });
        }
    }

    public void SendMessage(Dictionary<string, object> message, Action<Dictionary<string, object>> replyHandler, WKInterfaceLabel label)
    {
        if (validSession != null)
        {
            try
            {
                var NSValues = message.Values.Select(x => new NSString(JsonConvert.SerializeObject(x))).ToArray();
                var NSKeys = message.Keys.Select(x => new NSString(x)).ToArray();
                var NSMessage = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(NSValues, NSKeys);

                var reply = new WCSessionReplyHandler((replyMessage) =>
                {
                    var values = replyMessage.Values.Select(x => JsonConvert.SerializeObject(x)).ToArray();
                    var keys = replyMessage.Keys.Select(x => x.ToString()).ToArray();

                    var dict = keys.Zip(values, (k, v) => new { Key = k, Value = v })
                                   .ToDictionary(x => x.Key, x => (object)x.Value);

                    replyHandler.Invoke(dict);
                });

                validSession.SendMessage(NSMessage, reply, (error) =>
                {
                    label.SetText(error.ToString()); // I can see the error in here: "payload could n..."
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception sending message: {ex.Message}");
            }
        }
    }

    private void UpdateApplicationContextOnSession(NSDictionary<NSString, NSObject> NSApplicationContext)
    {
        NSError error;
        var sendSuccessfully = validSession.UpdateApplicationContext(NSApplicationContext, out error);
        if (sendSuccessfully)
        {
            Console.WriteLine($"Sent App Context from {Device} \nPayLoad: {NSApplicationContext.ToString()} \n");

            #if __IOS__
            Logging.Log("Success, payload: " + NSApplicationContext.ToString());
            #endif
        }
        else
        {
            Console.WriteLine($"Error Updating Application Context: {error.LocalizedDescription}");

            #if __IOS__
            Logging.Log("error: " + error.LocalizedDescription);
            #endif
        }
    }

    #endregion
like image 610
Drake Avatar asked Nov 12 '16 04:11

Drake


1 Answers

I solved it. Instead of implementing IWCSessionDelegate, I simply implement WCSessionDelegate instead and override the functions as needed.

like image 52
Drake Avatar answered Oct 22 '22 17:10

Drake