Search code examples
pathwatchkitnsurlwatchos-2watchconnectivity

Play Audio File in WatchKit OS2


Sending an MP3 from the phone:

NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);  
NSString *documentsDirectory = [pathArray objectAtIndex:0];  

NSString *yourSoundPath = [documentsDirectory stringByAppendingPathComponent:@"MyMusic.mp3"];  
NSURL *url = [NSURL fileURLWithPath:yourSoundPath isDirectory:NO];  
[self.session transferFile:url metadata:nil];  

How I've tried to receive and play the file on the watch:

-(void)session:(WCSession *)session didReceiveFile:(WCSessionFile *)file {  
    dispatch_async(dispatch_get_main_queue(), ^{  

        NSLog(@"URL%@" , file.fileURL.filePathURL);  

        NSDictionary *options = @{  
                                  WKMediaPlayerControllerOptionsAutoplayKey : @YES  
                                  };  

        [self presentMediaPlayerControllerWithURL:file.fileURL.filePathURL options:options completion:^(BOOL didPlayToEnd, NSTimeInterval endTime, NSError * __nullable error) {  
            if (!didPlayToEnd) {  
                NSLog(@"The player did not play all the way to the end. The player only played until time - %.2f.", endTime);  
            }  

            if (error) {  
                NSLog(@"There was an error with playback: %@.", error);  
            }  
        }];  
    });  
} 

This the file's URL:

file:///var/mobile/Containers/Data/PluginKitPlugin/-------/Documents/Inbox/com.apple.watchconnectivity/------/Files/----/MyMusic.mp3

This is the error:

There was an error with playback: Error Domain=com.apple.watchkit.errors Code=4 "The requested URL was not found on this server." UserInfo={NSUnderlyingError=0x16d4fa10 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}, NSLocalizedDescription=The requested URL was not found on this server.}.

How can I play this file in watchOS 2?


Solution

  • To enable the watch app to play the audio file, you'll have to move it to a shared app group container.

    You need to enable a shared app group between the WatchKit extension and WatchKit app.

    If you aren't putting the file in the shared container then it's expected that the audio playback would fail. You should also not dispatch away from the callback queue until after you've moved the file as WCSession will delete the file when the callback returns.

    Here is a tutorial that explains how to do it, and some information from Apple on this topic.

    Edit: Added example

    Something like this should work once you've enabled the app group container for your WatchKit App and Extension:

    - (void)session:(WCSession * __nonnull)session didReceiveFile:(WCSessionFile * __nonnull)file
    {
        NSLog(@"received file %@, metadata: %@", file, file.metadata);
    
        NSFileManager *fm = [NSFileManager defaultManager];
        NSError *error = nil;
        NSURL *containerURL = [fm containerURLForSecurityApplicationGroupIdentifier:@"<YOUR APP GROUP HERE>"];
        NSString *documentsPath = [containerURL path];
        NSString *dateString = [[[NSDate date] description] stringByAppendingString:@"-"];
        NSString *fileNameWithDate = [dateString stringByAppendingString:file.fileURL.lastPathComponent];
        documentsPath = [documentsPath stringByAppendingPathComponent:fileNameWithDate];
        if ([fm moveItemAtPath:[file.fileURL.path stringByExpandingTildeInPath] toPath:documentsPath error:&error]) {
            if ([fm fileExistsAtPath:documentsPath isDirectory:nil]) {
                NSLog(@"moved file %@ to %@", file.fileURL.path, documentsPath);
                dispatch_async(dispatch_get_main_queue(), ^{
                    NSDictionary *options = @{ WKMediaPlayerControllerOptionsAutoplayKey : @YES };
                    [self presentMediaPlayerControllerWithURL:[NSURL fileURLWithPath:documentsPath] options:options completion:^(BOOL didPlayToEnd, NSTimeInterval endTime, NSError * __nullable playAudioError) {
                        if (!didPlayToEnd) {
                            NSLog(@"The player did not play all the way to the end. The player only played until time - %.2f.", endTime);
                        }
    
                        if (playAudioError) {
                            NSLog(@"There was an error with playback: %@.", playAudioError);
                        }
                    }];
                 });
    
    
            } else {
                NSLog(@"failed to confirm move of file %@ to %@ (%@)", file.fileURL.path, documentsPath, error);
            }
        } else {
            NSLog(@"failed to move file %@ to %@ (%@)", file.fileURL.path, documentsPath, error);
        }
    }