Ross Freeman Ross Freeman - 7 months ago 86
Swift Question

WatchKit App Connectivity Issues

I'm trying to make a simple watch app that displays information sent by the companion app. I'm having some trouble getting the WatchKit app to properly receive the information.

On the sender side I have the following code:

- (void)viewDidLoad {
[super viewDidLoad];

// Prevents UIWebView from displaying under nav bar
self.navigationController.navigationBar.translucent = NO;

_timer = [NSTimer scheduledTimerWithTimeInterval:12.0 target:self selector:@selector(showAlert) userInfo:nil repeats:NO];

_diningLocations = [[NSMutableArray alloc] initWithCapacity:0];

if ([WCSession isSupported]) {
self.session = [WCSession defaultSession];
self.session.delegate = self;
[self.session activateSession];
// The following line works
//[self.session updateApplicationContext:@{@"hello" : @"world"} error:nil];
}

- (void)grabLocationsFromServer {
_query = [PFQuery queryWithClassName:self.tableName];

[MBProgressHUD showHUDAddedTo:self.view animated:YES];

[_query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// Do stuff

[self.locationTable reloadData];
[self loadWatchApp];
} else {
// Log details of the failure
NSLog(@"Error: %@ %@", error, [error userInfo]);
[MBProgressHUD hideHUDForView:self.view animated:YES];

}
[MBProgressHUD hideHUDForView:self.view animated:YES];
}];
}

- (void)loadWatchApp {

if(self.session) {

if (self.session.paired && self.session.watchAppInstalled) {
NSError *error;

[self.session updateApplicationContext:@{@"Locations": self.diningLocations} error:&error];
}
}

}


On the receiving end I have this simple code snippet:

func loadTableData(data: [RFDiningLocation]) {
table.setNumberOfRows(data.count, withRowType: "location")

for i in 0..<data.count {
let row = table.rowControllerAtIndex(i) as! RFDiningRowController
// row.label.setText(data[i].name)
row.label.setText("Hello, world!")
}
}


When I call updateApplicationContext in viewDidLoad, it works 100% fine, but when I call it from within another function the WatchKit app just doesn't respond. I can confirm that loadWatchApp is called as well as updateApplicationContext. Thanks!

Answer

I would implement the session function from WCSessionDelegate activationDidCompleteWithState and then in the callback if state is activated then call your grabLocationsFromServer function. If grabLocationsFromServer gets called before you activate the Watch session, that could be why your application context isn't being updated.

Also, it's suggested that you call activate session as soon as possible, such as in the init of the App Delegate.

I was just experimenting before you said this worked for you, so here's what I'm seeing:

  • On startup of watch (initial connection), there is a call to activationDidCompleteWithState but there is no context yet, so didReceiveApplicationContext is not called.
  • Watch app updates per expected applicationContext only occurs when I send an empty message in sessionReachabilityDidChange. My understanding is that I have to "awake" the phone app in the background by sending this initial message. This message can be ignored on the phone side.

    func sessionReachabilityDidChange(session: WCSession) {
      if session.reachable {
        sendMessage(["InitialMessage": true], replyHandler: nil, errorHandler: nil)
      }
    }