Michael Rowe Michael Rowe - 9 days ago 5
Swift Question

Debugging advice for WatchOS2

I've been going through the examples in WatchOS 2 By Tutorial book by the team over at RayWenderlich, specifically chapter 18. They all work fine. In my own App, I am trying to send a button press from the watch to fire a button on the iPhone App. Here's the relevant code in Swift from the Watch and the Phone:

Watch:

//
// InterfaceController.swift
// Wasted Time Extension
//
// Created by Michael Rowe on 7/21/15.
// Copyright © 2010-2015 Michael Rowe. All rights reserved.
//

import WatchKit
import WatchConnectivity
import Foundation


class InterfaceController: WKInterfaceController,WCSessionDelegate {

@IBOutlet var wasteLabel: WKInterfaceLabel!
@IBOutlet var costLabel: WKInterfaceLabel!
@IBOutlet var counter: WKInterfaceLabel!
@IBOutlet var statusButton: WKInterfaceButton!

// our watchconnective session
var session : WCSession?


override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
}

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

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

override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}

func session(session: WCSession, didReceiveMessage message: [String: AnyObject], replyHandler: [String: AnyObject] -> Void) {

print("Did receive message Watch \(message)")
}

@IBAction func addButtonPressed() {

// Pull values from the Phone for current meeting cost, waste costs, and people in meeting
let prefs:NSUserDefaults = NSUserDefaults(suiteName: "a.b.c")!

var counterd = prefs.doubleForKey("keyPeopleInMeeting")


counterd++
counter.setText(String(format:"%9.0f", counterd))

// Sending data to iPhone via Interactive Messaging
if WCSession.isSupported(){
// we have a watch supporting iPhone

let session = WCSession.defaultSession()

// we can reach the watch
if session.reachable {
let message = ["add": "1"]
print("Message \(message)")
session.transferUserInfo(message)
print("Send Message Add - People \(counterd)")
}
}

if WCSession.isSupported() {
let session = WCSession.defaultSession()
if session.reachable {
let message = ["add":"1"]
session.sendMessage(message, replyHandler: { ( reply: [String: AnyObject]) -> Void in
print("Reply: \(reply)")
}, errorHandler: { (error: NSError) -> Void in
print("ERROR Watch: \(error.localizedDescription)")
})
} else { // reachable
self.showReachabilityError()
}
}

print("Watch Add Button Pressed \(counterd)")
}

@IBAction func minusButtonPressed() {
// Pull values from the Phone for current meeting cost, waste costs, and people in meeting
let prefs:NSUserDefaults = NSUserDefaults(suiteName: "a.b.c")!

var counterd = prefs.doubleForKey("keyPeopleInMeeting")
counterd--
if (counterd <= 1) {
counterd = 1
}
counter.setText(String(format:"%9.0f", counterd))

if WCSession.isSupported() {
let session = WCSession.defaultSession()
if session.reachable {
let message = ["minus":"1"]
session.sendMessage(message, replyHandler: { ( reply: [String: AnyObject]) -> Void in
print("Reply: \(reply)")
}, errorHandler: { (error: NSError) -> Void in
print("ERROR Watch: \(error.localizedDescription)")
})
} else { // reachable
self.showReachabilityError()
}
}

print("Watch Minus Button Pressed \(counterd)")
}

func statusButtonPressed() {
// Pull values from the Phone for current meeting cost, waste costs, and people in meeting
let prefs:NSUserDefaults = NSUserDefaults(suiteName: "a.b.c")!

let status = statusButton.description

if WCSession.isSupported() {
let session = WCSession.defaultSession()
if session.reachable {
let message = ["status":status]
session.sendMessage(message, replyHandler: { ( reply: [String: AnyObject]) -> Void in
print("Reply: \(reply)")
}, errorHandler: { (error: NSError) -> Void in
print("ERROR Watch: \(error.localizedDescription)")
})
} else { // reachable
self.showReachabilityError()
}
}

print("Watch Status Button Pressed - Status \(statusButton)")
}

func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]){

let prefs:NSUserDefaults = NSUserDefaults(suiteName: "a.b.c")!

if let waste = applicationContext["waste"] as? Float {
print("Watch Receive - Waste \(waste)")
}

if let cost = applicationContext["cost"] as? Float {
print("Watch Receive - Cost \(cost)")
}

if let counternum = applicationContext["counter"] as? Float {
print("Watch Receive - Counter \(counternum)")
}

if let status = applicationContext["status"] as? String {
print("Watch Receive - Status \(status)")
statusButton.setTitle(status)
}

}

private func showReachabilityError() {
let tryAgain = WKAlertAction(title: "Try Again", style: .Default, handler: { () -> Void in })
let cancel = WKAlertAction(title: "Cancel", style: .Cancel, handler: { () -> Void in })
self.presentAlertControllerWithTitle("Your iPhone is not reachable.", message: "You cannot adjust the status or number of attendees Watch is not currently connected to your iPhone. Please ensure your iPhone is on and within range of your Watch.", preferredStyle: WKAlertControllerStyle.Alert, actions:[tryAgain, cancel])
}

func session(session: WCSession, didFinishUserInfoTransfer userInfoTransfer: WCSessionUserInfoTransfer, error: NSError?) {

print("Transfer User Info Error watch: \(error)")
}

}


And the receiving code on the
iPhone:CODE:

func session(session: WCSession,
didReceiveMessage message: [String : AnyObject],
replyHandler: ([String : AnyObject]) -> Void) {

if let counterd = message["add"] as? Float {
let reply = ["add":counterd]
print("iPhone Receive Add \(counterd)")
addButtonPressed(self)
replyHandler(reply)
}

if let counterd = message["minus"] as? Float {
let reply = ["minus":counterd]
print("iPhone Receive minus \(counterd)")
removeButtonPressed(self)
replyHandler(reply)
}

if let status = message["status"] as? String {
if status == "Start" {
let reply = ["status":"Quorum"]
meetingStartedButtonPressed(self)
replyHandler(reply)
}
if status == "Quorum" {
let reply = ["status": "Finish"]
quorumButtonPressed(self)
replyHandler(reply)
}
if status == "Finish" {
let reply = ["status": "Reset"]
meetingEndedButtonPressed(self)
replyHandler(reply)
}
if status == "Reset" {
let reply = ["status": "Start"]
resetButtonPressed(self)
replyHandler(reply)
}
print("iPhone Received Status Button \(status)")
}
}


I get the messages firing fine on the Watch and see them in the debug log... But they do not seem to fire on the Phone. The phone is successfully sending its messages to the watch.

I have tested this code both in the simulator and on my own watch and iPhone. Note that the messages from the iPhone to the Watch are done using the via updateApplicationContext vs. the send message I am trying to use to send messages from the watch to the iPhone. Here's a sample of the iPhone code for sending context:

if WCSession.isSupported() {
if session.watchAppInstalled {
let UserInfo = ["waste":Float((wastedAmount.text! as NSString).floatValue), "cost":Float((totalAmount.text! as NSString).floatValue), "counter":Float((peopleInMeeting.text! as NSString).floatValue), "status":"Start"]
do {
try session.updateApplicationContext(UserInfo as! [String : AnyObject])
} catch {
print("Updating the context failed: ")
}
}
}

Answer

More information is needed regarding specifically what you're actually seeing on the Watch, when you say:

I get the messages firing fine on the Watch and see them in the debug log... But they do not seem to fire on the Phone. The phone is successfully sending its messages to the watch.

However, one common occurrence is that the iPhone code is actually working correctly, and the only thing you are not seeing is the debug statements printed to the console. This seems likely to be the case since you say you are seeing the expected Watch messages, presumably including those from print("Reply: \(reply)"). This indicates the message is being handled by the iPhone.

When that's the case, it's often simply that you are expecting to see debug console messages from both the Watch and iOS simulator processes at the same time, but in fact you're only connected to one or the other. There are (at least) two things you can do here:

  1. Run the WatchKit app from Xcode, but then change to attach to the iPhone process instead. In Xcode, go Debug > Attach to Process... and select the iPhone app under "Likely Targets".
  2. Start by running the iPhone app, which will mean you are already attached to that process. In the Apple Watch simulator, run the Watch app. You'll then be able to debug the iPhone side of the communication.