MPMediaLibrary addItemWithProductID

I'm looking into the new APIs in MPMediaLibrary in iOS 9.3 and running into a few issues.


I am using SKCloudServiceController to request authorization and then check for the AddToCloudMusicLibrary capability. I then request authorization to the MPMediaLibrary (which appears not to do anything as the SKCloudServiceController auth already does that as far as I can tell...) and then try and do the following:


MPMediaLibrary.defaultMediaLibrary().addItemWithProductID(self.trackID, completionHandler: { (entities, error) in

})


This is giving me the following error:


Error Domain=MPErrorDomain Code=2 "The requested operation is not enabled for this device." UserInfo={NSLocalizedDescription=The requested operation is not enabled for this device.}


Initially I thought it may be a problem with my product ID although this doesn't seem to be the case as I can play the track just fine using the new setQueueWithStoreIDs method on MPMusicPlayerController.


There is very little in the way of documentation for these new APIs but I'm doing everything correctly as far as I can see. Can anyone shed some light on how I can add Apple Music tracks to the user's iCloud library?

Accepted Reply

In the end I managed to get this working on a different device with the exact same code. It works on an iPad Pro but not on an iPhone 5c - I suspect it may be tied to the 64-bit processor.

Replies

Make sure you have enabled "iCloud Music Library" inside "Settings->Music"


I had the same problem but afer enable that setting now works.

I have it enabled already. I tried turning it off and back on again to see if that cleared anything but still getting the same error:


Error Domain=MPErrorDomain Code=2 "The requested operation is not enabled for this device." UserInfo={NSLocalizedDescription=The requested operation is

not enabled for this device.}


Within the Music app, I am able to add songs to my iCloud Library (and they are appearing on other devices) so not sure why it isn't working from my own app.

check the result of your capabilities:

SKCloudServiceController *cloudServiceController;
cloudServiceController = [[SKCloudServiceController alloc] init];
[cloudServiceController requestCapabilitiesWithCompletionHandler:^(SKCloudServiceCapability capabilities, NSError * _Nullable error) {
        NSLog(@"%lu %@", (unsigned long)capabilities, error);
}];


As i understand the enumerator:


typedef NS_OPTIONS(NSUInteger, SKCloudServiceCapability) {
    SKCloudServiceCapabilityNone                           = 0, // NO APPLE MUSIC ACCOUNT
    SKCloudServiceCapabilityMusicCatalogPlayback           = 1 << 0, // APPLE MUSIC ACCOUNT WITHOUT ICLOUD MUSIC LIBRARY
    SKCloudServiceCapabilityAddToCloudMusicLibrary         = 1 << 8, // APPLE MUSIC ACCOUNT WITH ICLOUD MUSIC LIBRARY
} NS_AVAILABLE_IOS(9_3);

I'm using Swift and when I check if the capabilities contains .AddToCloudMusicLibrary I'm getting a positive response:


let controller = SKCloudServiceController()
controller.requestCapabilitiesWithCompletionHandler({ (capabilities, error) in
  if let error = error {
        NSLog("Capabilities error: \(error)")
        return
    }


  if capabilities.contains(.AddToCloudMusicLibrary) {
  NSLog("Can add to cloud music library")
  }
}


If I NSLog the capabilities variable, I'm getting a value of "257". If I turn "iCloud Music Library" off in my settings, then sure enough it doesn't show up as being contained within the capabilities.

In the end I managed to get this working on a different device with the exact same code. It works on an iPad Pro but not on an iPhone 5c - I suspect it may be tied to the 64-bit processor.

I am having trouble getting the authorization.... the requestAuthorization method doesn't seem to be working. How do I ask the user for permission to use SKCloudServiceController?? Becuase I can request for Capabilities, but then it says I am not authorized to do so.

GOT IT, wasnt requesting in Delegate. Silly me.

I'm really excited by this new feature. Thanks Apple! I wasn't able to find much documentation so I'm sharing what worked for me. I make the requests in the first part, then wait for the musicplayer to get both a nowPlayingItem and playbackDuration before accessing the info - mainly getting the mediaItem for future reference. Hope this helps anyone still looking for a way to start.


-(void) submitAppleMusicTrackWithProductID: (NSString *) productID // productID in US is the last numbers after i= in the share URL from Apple Music  
{
    NSLog(@"submitAppleMusic has been called for productID: %@", productID);
    [SKCloudServiceController requestAuthorization:^(SKCloudServiceAuthorizationStatus status) {
        NSLog(@"status is %ld", (long)status);
        SKCloudServiceController *cloudServiceController;
        cloudServiceController = [[SKCloudServiceController alloc] init];
        [cloudServiceController requestCapabilitiesWithCompletionHandler:^(SKCloudServiceCapability capabilities, NSError * _Nullable error) {
            NSLog(@"%lu %@", (unsigned long)capabilities, error);
      
            if (capabilities >= SKCloudServiceCapabilityAddToCloudMusicLibrary)
            {
                NSLog(@"You CAN add to iCloud!");
                [[MPMediaLibrary defaultMediaLibrary] addItemWithProductID:productID completionHandler:^(NSArray<__kindof MPMediaEntity *> * _Nonnull           entities, NSError * _Nullable error) {
              
                    NSLog(@"added id%@ entities: %@ and error is %@", productID, entities, error);
                    NSArray *tracksToPlay = [NSArray arrayWithObject:productID];
                    [[MPMusicPlayerController systemMusicPlayer] setQueueWithStoreIDs:tracksToPlay];
                    [[MPMusicPlayerController systemMusicPlayer] play];
              
                    [self performSelectorOnMainThread:@selector(getInfoFromAddedAppleMusicTrack:) withObject:productID waitUntilDone:YES];
              
                }];
            }
            else
            {
                NSLog(@"Blast! The ability to add Apple Music track is not there. sigh.");
            }
      
        }];
  
    }];
}
-(void) getInfoFromAddedAppleMusicTrack: (NSString *) productID
{
    NSLog(@"FYI - musicplayer duration is: %f", [[[MPMusicPlayerController systemMusicPlayer] nowPlayingItem] playbackDuration]);
    //need to check for both the nowPlaying item and if there is a reported playbackDuration, as there is a variable time between a storeMediaItema and a concreteMediaItem
    if (([[MPMusicPlayerController systemMusicPlayer] nowPlayingItem]) && ([[[MPMusicPlayerController systemMusicPlayer] nowPlayingItem] playbackDuration]))
    {
  
        track *t1 = [[track alloc] init];
        t1.mediaitem = [[MPMusicPlayerController systemMusicPlayer] nowPlayingItem];
        t1.appleProductIDURL = productID;
        t1.start = 0;
        t1.crossfade = 0;
        t1.end = [[[[MPMusicPlayerController systemMusicPlayer] nowPlayingItem] valueForProperty:MPMediaItemPropertyPlaybackDuration] intValue];
        t1.name = [[[MPMusicPlayerController systemMusicPlayer] nowPlayingItem] valueForProperty:MPMediaItemPropertyTitle];
        t1.trackArtist = [[[MPMusicPlayerController systemMusicPlayer] nowPlayingItem] valueForProperty:MPMediaItemPropertyArtist];
  
        //track t1 is now fully stocked with info from the track you've added
    }
    else
    {
        NSLog(@"seems the track is not fully loaded so try again in 1 second");
  
        [self performSelector:@selector(getInfoFromAddedAppleMusicTrack:) withObject:productID afterDelay:1.0];
  
        // count loops and jump out if something is wrong - I've never seen more that 7 seconds needed
    }
}

How do you get the Product Id. I understand that we can use MPMediaPlayerController to play with Product Id. I am not sure how to fetch the Product id of the apple music item. Can you provide me the detail

I hope someone else will respond with a more generally useful method, but I ask the user to 'share URL' of the desired track, grab the Apple Music URL from the clipboard, parse the URL for the productID, then save it with the track info in my app's (MyFitnessDJ) playlist. I haven't yet found a way to get the product ID programatically with typical track data like title and artist.


Could someone else offer a better way?

I'm getting a different error when trying to add a song to a playlist.


Error Domain=MPErrorDomain Code=0 "An unknown error occurred." UserInfo={NSLocalizedDescription=An unknown error occurred.}


I have SKCloudServiceCapabilityAddToCloudMusicLibrary capability and am able to play any song from Apple Music. But for some reason, I get this error when I call:


[MPMediaPlaylist addItemWithProductID:]


Anyone knows how to help?


I'm currently testing this on an iPhone 6s running iOS 9.3.

Could you please post me how productID looks. How you are getting this porductID?


Currently I am using productID like this one: 559334751. When I am trying to add item with product ID gettins this error:

Error Domain=MPErrorDomain Code=5 "The requested action is not supported" UserInfo={NSLocalizedDescription=The requested action is not supported}

I am having the same error. Did you figure out where was a problem?

Nope! Still getting error code 0.

Hey I am having the same error, it`s very frustrating.