I have a WKInterfaceLabel on my WatchKit App storyboard. Every 30 seconds, the WatchKit App will send a request to the parent iPhone app to get a string. Upon receiving the string, I set the string as the WKInterfaceLabel's text. Code looks like this:
NSDictionary *getTokenRequest = @{@"request":@"getToken"};
[InterfaceController openParentApplication:getTokenRequest reply:^(NSDictionary *replyInfo, NSError *error) {
    if (error) {
        NSLog(@"%@", error);
    } else {
        token = replyInfo[@"token"];
        NSLog(@"token=%@", token); // I am getting the correct token
        // But the following line doesn't update label text
        [self refreshToken];
    }
}];
- (void)refreshToken {
    if ([token length] != 0) {
        [self.codeLabel setText:token];
    }
}
When app launches or resumes, the label will display the correct string returned from parent app. This is due to calling refreshToken in willActivate. What's not working is when I request data from the parent app every 30 seconds, I am also calling refreshToken in the callback of the request as shown in the code above, even though I am getting the correct string (token here) from the phone app and calling [self.codeLabel setText:token]. It's the same method call: when called within willActivate, setText updates the label fine, but when called in the callback method of a request, the label text UI doesn't update. 
This issue happens only on device. When running on simulator, the label text updates every 30 seconds as expected.
When I was developing an iPhone app, I once had a similar issue, and it was resolved by calling the setText method on the main thread: 
[self performSelectorOnMainThread:@selector(refreshToken) withObject:nil waitUntilDone:NO]; 
Sadly, the same fix does not work for Apple Watch.
I have also found a similar thread here: can setText for WKInterfaceLabel in init method but not in didselectrow method however it does not provide a good solution.
Any suggestions/thoughts are appreciated. Thanks in advance!
                        
In the end, this is what worked for me:
Thanks to this post http://iosdevelopmentjournal.com/blog/2013/01/16/forcing-things-to-run-on-the-main-thread/.
In short, when UI is not updating when you tell it to, it might not be scheduled to run on the main thread.
As shown in my question, I have already suspected the threading issue and tried to use
performSelectorOnMainThreadbut it didn't work. For those curious, here's the difference betweenperformSelectorOnMainThreadanddispatch_async: Whats the difference between performSelectorOnMainThread and dispatch_async on main queue?