Create a custom MediaPickerController - objective-c

I was tasked to create a music player application that is sort of like a DJ app, and when the add music button is clicked, it needs to show the list of all the songs just like the native MediaPickerController but with added functionalities like sorting, search and not fullscreen which is not available in the native one.
I tried scouring the internet for an answer but I can't find anything about this, it's all about creating the native media picker controller.
I found about MPMediaQuery which allows you to get the songs list in the phone, but I can't use it in a for-in
Sample:
MPMediaQuery *songs = [MPMediaQuery songsQuery];
for (MPMediaItem *item in songs) {
}
But I get this:
*Collection expression type 'MPMediaQuery ' may not respond to 'countByEnumeratingWithState:objects:count:'
Any suggestions?

About the mediaquery, you need to convert it to nsarray first, try this:
MPMediaQuery *songs = [MPMediaQuery songsQuery];
NSArray *songList = [songs items];
for (MPMediaItem *item in songList) {
}

Related

Apple Music API - Create a Playlist

I have been exploring the Apple Music API to see what kind of functionality I can expect to be able to use in an iOS app. I have created a little test app that gains permission from the user and outputs the playlists I have (and songs) to NSLog.
MPMediaQuery *myPlaylistsQuery = [MPMediaQuery playlistsQuery];
[myPlaylistsQuery setGroupingType:MPMediaGroupingPlaylist];
NSArray *playlists = [myPlaylistsQuery collections];
for (MPMediaPlaylist *playlist in playlists) {
NSLog (#"%#", [playlist valueForProperty: MPMediaPlaylistPropertyName]);
NSArray *songs = [playlist items];
for (MPMediaItem *song in songs) {
NSString *songTitle =
[song valueForProperty: MPMediaItemPropertyTitle];
NSLog (#"\t\t%#", songTitle);
}
}
From this, I have been able to deduce the following (but I'm not 100% certain):
the playlist (basic info: name, id) is stored locally on the device
the playlist songs are also pulled from local storage but if the playlist hasn't been downloaded to the device it goes off to Apple to grab the song list.
So far, so good. What I want to know is:
is there a way of creating a playlist from my app (via the API)?
I know there is an MPMediaPlaylist addItem and add method but can't seem to find a way of creating the new playlist itself.
According to this page it should be possible: https://affiliate.itunes.apple.com/resources/blog/apple-music-api-faq/
Can a developer create brand new playlists on the user’s device with the Apple Music API?
Yes. The API allows develops to new create playlists on the user’s device.
I've figured this out. If you use the following code you can generate a new playlist and perform an action on it.
NSUUID *uuid = [NSUUID UUID]; //uuid for the playlist
[[MPMediaLibrary defaultMediaLibrary] getPlaylistWithUUID:uuid creationMetadata:[[MPMediaPlaylistCreationMetadata alloc] initWithName:#"YOUR PLAYLIST NAME"] completionHandler:^(MPMediaPlaylist * _Nullable playlist, NSError * _Nullable error) {
NSLog(#"%#", error);
if (!error) {
NSLog(#"All ok let's do some stuff with the playlist!");
}
}];
Apple's documentation on the whole API is severely lacking in terms of sample code and practical examples!

Play songs by album/artist on iTunes with Scripting Bridge/Obj-C

Given a name of an album or artist I would like to tell iTunes to play all songs by that artist or on that album. How can I do this? I know how to get all the songs to play by filtering an array with all the tracks, but how do I tell iTunes to play only those I want?
EDIT
I know I can use this code to get all the tracks I want to play, but I have no idea how to tell iTunes to play them in sucession. Any ideas?
// Get the app
iTunesApplication* iTunes = [SBApplication applicationWithBundleIdentifier: #"com.apple.iTunes"];
// Get the library
iTunesSource* library;
for (iTunesSource* thisSource in [iTunes sources]) {
if ([thisSource kind] == iTunesESrcLibrary) {
library = thisSource;
break;
}
}
SBElementArray* tracks;
for (iTunesPlaylist* playlist in [library playlists]) {
if ([playlist specialKind] == iTunesESpKMusic) {
tracks = [playlist searchFor: name only: type == 0 ? iTunesESrAAlbums : iTunesESrAArtists];
}
}
// There. Now what? how do I play all the tracks in 'tracks'?
You can play one songs.
iTunesTrack *track = [tracks objectAtIndex:0];
[track playOnce:YES];
If you want play several songs, you should create or use a playlist:
for (iTunesUserPlaylist *thisList in playlists) {
if ([[thisList name] isEqualToString:playlistName]) {
playlist = thisList;
break;
}
}
[track duplicateTo:playlist];
[playlist playOnce:YES];
So it seems iTunes does not have an AppleScript/Scripting Bridge command to play songs by a certain artist/on a certain album. And according to this question, it's not possible to tell iTunes to play a custom list of tracks without creating a new playlist (which I don't want to do). In short, there's really no way to solve this problem...

Play specific title in iTunes via ScriptingBridge

I'm trying to write an application that interacts with iTunes via ScriptingBridge. I works well so far, but the options of this method seem to be very limited.
I want to play song with a given name, but it looks like there's no way to do this. I haven't found anything similar in iTunes.h…
In AppleScript it's just three lines of code:
tell application "iTunes"
play (some file track whose name is "Yesterday")
end tell
And then iTunes starts to play a classic Beatles song.
Is there any was I can do this with ScriptingBridge or do I have to run this AppleScript from my app?
It's not as simple as the AppleScript version, but it's certainly possible.
Method one
Get a pointer to the iTunes library:
iTunesApplication *iTunesApp = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
SBElementArray *iTunesSources = [iTunesApp sources];
iTunesSource *library;
for (iTunesSource *thisSource in iTunesSources) {
if ([thisSource kind] == iTunesESrcLibrary) {
library = thisSource;
break;
}
}
Get an array containing all the audio file tracks in the library:
SBElementArray *libraryPlaylists = [library libraryPlaylists];
iTunesLibraryPlaylist *libraryPlaylist = [libraryPlaylists objectAtIndex:0];
SBElementArray *musicTracks = [self.libraryPlaylist fileTracks];
Then filter the array to find tracks with the title you're looking for.
NSArray *tracksWithOurTitle = [musicTracks filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K == %#", #"name", #"Yesterday"]];
// Remember, there might be several tracks with that title; you need to figure out how to find the one you want.
iTunesTrack *rightTrack = [tracksWithOurTitle objectAtIndex:0];
[rightTrack playOnce:YES];
Method two
Get a pointer to the iTunes library as above. Then use the Scripting Bridge searchFor: only: method:
SBElementArray *tracksWithOurTitle = [library searchFor:#"Yesterday" only:kSrS];
// This returns every song whose title *contains* "Yesterday" ...
// You'll need a better way to than this to pick the one you want.
iTunesTrack *rightTrack = [tracksWithOurTitle objectAtIndex:0];
[rightTrack playOnce:YES];
Caveat to method two: The iTunes.h file incorrectly claims that the searchFor: only: method returns an iTunesTrack*, when in fact (for obvious reasons) it returns an SBElementArray*. You can edit the header file to get rid of the resulting compiler warning.

Getting name of track that is currently being played

Is there a way to get the name of the track that is currently being played in iTunes on an iOS device?
I haven't found anything too useful inMPMusicPlayerController nor AVAudioPlayer.
Thanks!
Alright, after some more searching, I found the answer hidden in this semi-related question: Get album artwork from MP3 file/ID3 tag
There are two properties that exist in MPMusicPlayerController that provides us with the Track Name and Track Artist. They are MPMediaItemPropertyTitle and MPMediaItemPropertyArtist, respectively.
The accepted answer's answer contains 5x the code you need to get what you're after. Here's a simpler answer:
if ([MPMusicPlayerController iPodMusicPlayer].playbackState == MPMusicPlaybackStatePlaying) {
MPMediaItem *item = [[MPMusicPlayerController iPodMusicPlayer] nowPlayingItem];
NSString *title = [item valueForProperty:MPMediaItemPropertyTitle];
NSString *artist = [item valueForProperty:MPMediaItemPropertyArtist]; // common
// do something with these
} else {
// nothing is playing!
}

Get list of installed apps on iPhone

Is there a way (some API) to get the list of installed apps on an iPhone device.
While searching for similar questions, I found some thing related to url registration, but I think there must be some API to do this, as I don't want to do any thing with the app, I just want the list.
No, apps are sandboxed and Apple-accepted APIs do not include anything that would let you do that.
You can, however, test whether a certain app is installed:
if the app is known to handle URLs of a certain type
by using [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:#"thisapp://foo"]
You can get a list of apps and URL schemes from here.
For jailbroken devices you can use next snipped of code:
-(void)appInstalledList
{
static NSString* const path = #"/private/var/mobile/Library/Caches/com.apple.mobile.installation.plist";
NSDictionary *cacheDict = nil;
BOOL isDir = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath: path isDirectory: &isDir] && !isDir)
{
cacheDict = [NSDictionary dictionaryWithContentsOfFile: path];
NSDictionary *system = [cacheDict objectForKey: #"System"]; // First check all system (jailbroken) apps
for (NSString *key in system)
{
NSLog(#"%#",key);
}
NSDictionary *user = [cacheDict objectForKey: #"User"]; // Then all the user (App Store /var/mobile/Applications) apps
for (NSString *key in user)
{
NSLog(#"%#",key);
}
return;
}
NSLog(#"can not find installed app plist");
}
for non jailbroken device, we can use third party framework which is called "ihaspp", also its free and apple accepted. Also they given good documentation how to integrate and how to use. May be this would be helpful to you. Good luck!!
https://github.com/danielamitay/iHasApp
You could do this by using the following:
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector = NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSMutableArray *Allapps = [workspace performSelector:selectorALL];
NSLog(#"apps: %#", Allapps);
And then by accessing each element and splitting it you can get your app name, and even the Bundle Identifier, too.
Well, not sure if this was available back when the last answer was given or not (Prior to iOS 6)
Also this one is time intensive, yet simple:
Go into settings > Gen. >usage. The first category under usage at least right now is Storage.
It will show a partial list of apps. At the bottom of this partial list is a button that says "show all apps".
Tap that and you'll have to go through screen by screen, and take screenshots (Quick lock button and home button takes a screenshot).
I'm doing this now and I have hundreds of apps on my iPhone. So it's going to take me a while. But at least at the end of the process I'll have Images of all my apps.