A local network connection from the Apple Watch app to another device works on the simulator, but not on the Watch
I am trying to create an app for my Apple Watch to remotely control a media player (omxplayer) on a Raspberry Pi.
I got it in a simulator in Xcode, but it doesn't work on the actual clock.
On a Raspberry Pi, I have a simple python script that starts a TCP server and listens for commands.
In the InterfaceController.m file of the WatchKit renewal project, I have the following code:
- (IBAction)playPauseButtonPressed {
[self initNetworkCommunication];
[self sendNetworkCommand:@"play"];
[self closeNetworkCommunication];
}
-(void)initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"192.168.10.116", 10010, &readStream, &writeStream);
if(!CFWriteStreamOpen(writeStream)) {
NSLog(@"Error, writeStream not open");
return;
}
inputStream = (NSInputStream *)CFBridgingRelease(readStream);
outputStream = (NSOutputStream *)CFBridgingRelease(writeStream);
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
-(void)sendNetworkCommand:(NSString *) command {
NSData *data = [[NSData alloc] initWithData:[command dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
-(void)closeNetworkCommunication {
[inputStream close];
[outputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream setDelegate:nil];
[outputStream setDelegate:nil];
inputStream = nil;
outputStream = nil;
}
When I click a button in the My Watch app, "playPauseButtonPressed" is called. This connection initiates a network connection to the Raspberry Pi and sends a string.
As I said, it works on the Watch simulator, but not on a real device.
Please note that the same code works in an iOS app on a real device.
Any help would be appreciated!
source to share
Ok, I started working. In the meantime, I have upgraded to WatchKit 2.0 SDK. Here is my code (still needs work) in case anyone is interested:
ExtensionDelegate.m clock extensions:
- (void)applicationDidFinishLaunching
{
// Perform any final initialization of your application.
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
}
InterfaceController.m clock extensions:
- (IBAction)playPauseButtonPressed
{
[self sendCommandToPhone:@"pause"];
}
- (IBAction)seekBackwardButtonPressed
{
[self sendCommandToPhone:@"seek_backward"];
}
- (IBAction)seekForwardButtonPressed
{
[self sendCommandToPhone:@"seek_forward"];
}
- (IBAction)subtitleButtonPressed
{
[self sendCommandToPhone:@"subtitle"];
}
- (void)sendCommandToPhone:(NSString *)command
{
NSLog(@"sendCommandToPhone called");
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:command, @"command", nil];
[[WCSession defaultSession] sendMessage:dict
replyHandler:^(NSDictionary *replyHandler) {
}
errorHandler:^(NSError *error) {
NSLog(@"%@", [error localizedDescription]);
}
];
}
ViewController.m iPhone app:
- (void)viewDidLoad
{
[super viewDidLoad];
// Init communication with watch
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
NSLog(@"WCSession initialized");
}
}
// Message received from the watch
- (void)session:(WCSession * _Nonnull)session didReceiveMessage:(NSDictionary<NSString *, id> * _Nonnull)message
{
NSLog(@"didReceiveMessage called");
[self sendCommandToPlayerAndCloseConnection:message[@"command"]];
}
- (IBAction)connectButtonPressed:(id)sender
{
[self sendCommandToPlayer:@"list_dir"];
[self performSegueWithIdentifier:@"toFileTableViewController" sender:self];
}
- (IBAction)seekBackwardButtonPressed:(id)sender
{
[self sendCommandToPlayerAndCloseConnection:@"seek_backward"];
}
- (IBAction)seekForwardButtonPressed:(id)sender
{
[self sendCommandToPlayerAndCloseConnection:@"seek_forward"];
}
- (IBAction)stopButtonPressed:(id)sender
{
[self sendCommandToPlayerAndCloseConnection:@"stop"];
}
- (IBAction)playPauseButtonPressed:(id)sender
{
[self sendCommandToPlayerAndCloseConnection:@"pause"];
}
- (void)sendCommandToPlayer:(NSString *) command
{
NSLog(@"%@", [NSString stringWithFormat:@"Sending command to player : %@", command]);
[self initNetworkCommunication];
NSData *data = [[NSData alloc] initWithData:[command dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
- (void)sendCommandToPlayerAndCloseConnection:(NSString *) command
{
[self sendCommandToPlayer:command];
[self closeNetworkCommunication];
}
-(void)initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"192.168.10.116", 10023, &readStream, &writeStream);
if(!CFWriteStreamOpen(writeStream)) {
NSLog(@"Error, writeStream not open");
return;
}
inputStream = (__bridge NSInputStream *) readStream;
outputStream = (__bridge NSOutputStream *) writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
-(void)closeNetworkCommunication
{
[inputStream close];
[outputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream setDelegate:nil];
[outputStream setDelegate:nil];
inputStream = nil;
outputStream = nil;
}
Now I can control omxplayer from my phone and my watch;)
source to share