Search code examples
iosswiftwatchkitwatchoswatchconnectivity

How can I continue to receive messages/data from apple watch after dismissing a secondary view contoller


I am writing an application that allows me to record my golf strokes and save them, so that I can view the scores on an iPhone. The problem is that while the app does receive messages after launching and opening secondary view controllers, it stops listening for messages after dismissing any secondary view controller. If I then switch to a secondary view controller, it will listen for messages until I dismiss it again. I do not know why the root View controller will only listen until a secondary view controller is opened.

I am using sendMessage and its corresponding receiver function to transfer the number of stokes between devices. Each view controller contains the receive message function.

The code in question is as follows:

import UIKit // or WatchKit on Apple watch
import WatchConnectivity

class ViewController: UIViewController, WCSessionDelegate {

var session: WCSession!
let userData = NSUserDefaults.standardUserDefaults()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    ScrollHoles.contentSize = CGSize(width: 360,height: 1090)
    if (WCSession.isSupported()){
        self.session = WCSession.defaultSession()
        self.session.delegate = self
        self.session.activateSession()
        userData.synchronize()
    }
}

The send message function is...

// Save Data From Strokes To standatdUserDefaults.
@IBAction func SaveButton(sender: AnyObject) {
    let hole1Score = Strokes1.text;
    userData.setObject(hole1Score, forKey: "hole1Strokes")
    userData.synchronize()
    session.sendMessage(["score1A": hole1Score!], replyHandler: nil, errorHandler: nil) // score1A becomes score1B on Apple watch
}

and the receive message function is...

// Recieve Data from Apple Watch And Save It To standardUserDefaults (e.g. # of strokes).
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    let hole1Msg = message["score1B"] as? String // score1B becomes score1A on apple watch
    let hole2Msg = message["score2B"] as? String

    if (hole1Msg != nil){
        userData.setObject(hole1Msg, forKey: "hole1Strokes")
    }
    if (hole2Msg != nil){
        userData.setObject(hole2Msg, forKey: "hole2Strokes")
    }

The constants and if statement continue for all 18 holes.

I am using the following to dismiss secondary view controllers.

@IBAction func backButton(sender: AnyObject) {
    self.dismissViewControllerAnimated(true, completion: {});
}

Solution

  • As explained in this answer, WCSession only supports a single delegate at a time.

    Since you're setting up your session within view controllers, the session delegate is changing when you load another view controller. Although you may dismiss the secondary controller, it is still the delegate for the session.

    This is why your root view controller no longer receives messages, as it is no longer the session delegate.

    Recommended approach by Apple engineers:

    As the previous answer and developer forum posts suggest, you can create a session wrapper which you activate in the phone's AppDelegate, have it handle app-wide messages from your watch, and then either update a data store, or directly pass that data on to observers.

    In general, it's advantageous to setup Watch Connectivity as soon as possible (at launch, instead of in a specific view controller).

    Off-site tutorial:

    Natasha The Robot has an excellent tutorial which walks through using a Watch Connectivity session manager and data source in great detail.