BlastWave
BlastWave

Reputation: 797

WatchKit_Extension.InterfaceController does not implement delegate method

I am trying to learn watchkit connectivity using a simple app and have spent two days vainly researching this error:

{app name}_WatchKit_Extension.InterfaceController does not implement delegate method.

I have slavishly copied code from the "SimpleWatchConnectivity" https://developer.apple.com/library/content/samplecode/SimpleWatchConnectivity/Listings/SimpleWatchConnectivity_WatchKit_Extension_InterfaceController_swift.html demo code. and have scoured the forums without success.

My code builds and runs OK but I get the following error on the watch

2018-01-06 10:39:20.522995+1100 messageDemo WatchKit Extension[338:743006] [WC] -[WCSession onqueue_handleDictionaryMessageRequest:withPairingID:]_block_invoke delegate messageDemo_WatchKit_Extension.InterfaceController does not implement delegate method
2018-01-06 10:39:20.524378+1100 messageDemo WatchKit Extension[338:743006] [WC] -[WCSession _onqueue_sendResponseError:identifier:dictionaryMessage:] identifier: A150D814-453C-44B7-B970-913697526D6A with WCErrorCodeDeliveryFailed

when I execute:

if (WCSession.isSupported())  
    {  
        WCSession.default.delegate = self;  
        WCSession.default.activate()  

        if let messageText =  textField.text{  
             WCSession.default.sendMessage(["message": messageText]  
                ,replyHandler: {replyMessage in  
                    print(replyMessage)  
             }  

               , errorHandler: {error in  
                print( error.localizedDescription)}  )  
        }  
    }  

from the iOS ViewController.

My interface controller is as follows:

 import WatchKit  
import Foundation  
import WatchConnectivity  
class InterfaceController: WKInterfaceController  
    , WCSessionDelegate  
    {  
    func session(_ session: WCSession  
        , activationDidCompleteWith activationState: WCSessionActivationState  
        , error: Error?) {  
        print("activationDidCompleteWith:\(activationState )") /  
    }  
    func sessionReachabilityDidChange(_ session: WCSession) {  
      /  
      /  
        print("sessionReachabilityDidChange")  
    }  

    @IBOutlet var messageLabel: WKInterfaceLabel!  
    override func awake(withContext context: Any?) {  
        super.awake(withContext: context)  

        /  
    }  

    override func willActivate() {  
        /  
        super.willActivate()  
        if (WCSession.isSupported())  
        {  
            WCSession.default.delegate = self;  
            WCSession.default.activate()  

        }  

    }  

    override func didDeactivate() {  

        super.didDeactivate()  

    }  

    private func session(session: WCSession, didReceiveMessage message: [String : Any]  
        , replyHandler: @escaping ([String : Any]) -> Void) {  
        self.messageLabel.setText(message["message"]! as? String)  
    }  
       func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {  
        self.messageLabel.setText(message["message"]! as? String)  
    }  
    func session(_ session: WCSession, didReceiveMessageData messageData: Data) {  
        print("HERE01")  
    }  

    func session(_ session: WCSession, didReceiveMessageData messageData: Data, replyHandler: @escaping (Data) -> Void) {  
        self.session(session, didReceiveMessageData: messageData)  

         print("HERE02")  
    }  
    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {  

         print("HERE03")  
    }  

    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {  
         print("HERE04")  

    }  
    func session(_ session: WCSession, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?) {  
        print("HERE05")  
    }  

    func session(_ session: WCSession, didReceive file: WCSessionFile) {  
        print("HERE06")  
        }  

    func session(_ session: WCSession, didFinish fileTransfer: WCSessionFileTransfer, error: Error?) {  
        print("HERE07")  
    }  
}  

(Sorry about the formatting, I tried for 10 mins to get it right, but sadly failed)

I have added a bunch of print's to ensure all cases are covered.

the full error is:

2018-01-06 10:39:20.522995+1100 messageDemo WatchKit Extension[338:743006] [WC] -[WCSession onqueue_handleDictionaryMessageRequest:withPairingID:]_block_invoke delegate messageDemo_WatchKit_Extension.InterfaceController does not implement delegate method
2018-01-06 10:39:20.524378+1100 messageDemo WatchKit Extension[338:743006] [WC] -[WCSession _onqueue_sendResponseError:identifier:dictionaryMessage:] identifier: A150D814-453C-44B7-B970-913697526D6A with WCErrorCodeDeliveryFailed

Any assistance will be greatly appreciated

[EDIT] When I monitor the Phone app with the debugger, I see the send message fails with:

2018-01-07 09:47:23.319221+1100 messageDemo[6628:1549809] [WC] -[WCSession _onqueue_notifyOfMessageError:messageID:withErrorHandler:] (null) errorHandler: YES with WCErrorCodeSessionNotActivated
2018-01-07 09:47:23.321103+1100 messageDemo[6628:1549978] [WC] -[WCSession _onqueue_notifyOfMessageError:messageID:withErrorHandler:]_block_invoke dropping as pairingIDs no longer match. pairingID (null), client pairingID: (null)
WatchConnectivity session has not been activated.

The error I receive from the watchKitExtension quoted at the top of the post is generated when I attempt to send a second message. There is no error in watchKitExtension from the first message.

Upvotes: 4

Views: 4263

Answers (3)

lajosdeme
lajosdeme

Reputation: 2407

I had the same problem:

In my app I'm sending an image from iPhone to iWatch, and when the image is delivered I'm showing a notification on the iPhone. If an error occurs I'm showing an error message. For these reasons I needed to send back a reply to the app.

I solved it like this:

In the Watch Extension I implemented the following method:

    func session(_ session: WCSession, didReceiveMessageData messageData: Data, replyHandler: @escaping (Data) -> Void) {
    print("recieved data message to watch")

    //I have a method here which saves the image to watch storage...

   //I'm setting the reply data like this:
    let replyData = "Image recieved".data(using: .utf8)

    replyHandler(replyData!)
}

And back in the iOS app I'm handling the response like this:

 wcSession.sendMessageData(data, replyHandler: { (replyData) in

                let replyString = String(data: replyData, encoding: String.Encoding.utf8)

                if replyString != nil {
                 //Do something with the replyString...
                }

             }) { (error) in
                //Display error notification to user...
                print("error", error.localizedDescription)
            }

UPDATE: The above answer is still valid for dealing with reply handler. Just a quick note: Upon running the app in the simulator I noticed that the user has to wait a long time for the reply to arrive from the watch. This is not really optimal UX (especially if you need to update the UI instantaneously) so I opted for a much simpler option by checking if the watch is reachable by calling wcSession.isReachable and displaying the alert if true. So it's worth noting that the reply handler should be used only when you actually need the message the watch sends back.

Upvotes: 0

iGatiTech
iGatiTech

Reputation: 2268

Below Solution works well to share data between iPhone and iWatch via WCSession,

ViewController.swift

import UIKit
import WatchConnectivity

class ViewController: UIViewController, WCSessionDelegate {

    @IBOutlet weak var textFieldMessage : UITextField!
    @IBOutlet weak var buttonSend : UIButton!
    var wcSession : WCSession!

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

        wcSession = WCSession.default
        wcSession.delegate = self
        wcSession.activate()
    }

    //MARK: - Button Actions

    @IBAction func clickSendMessage(_ sender : UIButton) {

        let message = ["message" : textFieldMessage.text!]
        do {
            try wcSession.updateApplicationContext(message)
        } catch {
            print("Something went wrong")
        }
    }

    // MARK: - WCSessionDelegate

    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        NSLog("%@", "activationDidCompleteWith activationState:\(activationState) error:\(String(describing: error))")
    }

    func sessionDidBecomeInactive(_ session: WCSession) {
        print("%@", "sessionDidBecomeInactive: \(session)")
    }

    func sessionDidDeactivate(_ session: WCSession) {
        print("%@", "sessionDidDeactivate: \(session)")
    }

    func sessionWatchStateDidChange(_ session: WCSession) {
        print("%@", "sessionWatchStateDidChange: \(session)")
    }
}

enter image description here

InterfaceController.swift(Watch Extension)

import WatchKit
import Foundation
import WatchConnectivity

class InterfaceController: WKInterfaceController, WCSessionDelegate {

    var session : WCSession?
    @IBOutlet weak var sessionLabel : WKInterfaceLabel!

    override func willActivate() {
        // This method is called when watch view controller is about to be visible to user
        super.willActivate()

        session = WCSession.default
        session?.delegate = self
        session?.activate()
    }

    // MARK: - WCSessionDelegate

    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        NSLog("%@", "activationDidCompleteWith activationState:\(activationState) error:\(String(describing: error))")
    }

    func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
        NSLog("didReceiveApplicationContext : %@", applicationContext)
        sessionLabel.setText(applicationContext["message"] as? String)
    }

enter image description here

OutPut will be as below,

enter image description here

Upvotes: 8

BlastWave
BlastWave

Reputation: 797

My problem was that the sendMessage implemented replyHandler: {replyMessage in
print(replyMessage)
and I wasn't sending a reply on receipt of the message. I never actually worked out how to send a reply and finally abandoned sendMessage in favour of updateApplicationContext which more precisely addressed my requirements.

Upvotes: 1

Related Questions