Number of Albums By Artists - iphone

That's my Question =)
MPMediaQuery *artistQuery = [MPMediaQuery artistsQuery];
NSArray *songsByArtist = [artistQuery collections];
How can I get the number of albums of each artists of the MPMediaItemCollections in songsByArtist ?
Exemple :
The Beatles
3 Albums
AC/DC
6 Albums
Thank You !!

I get number of albums and songs for artist using predicate:
MPMediaPropertyPredicate *artistNamePredicate = [MPMediaPropertyPredicate predicateWithValue:#"ArtistName" forProperty:MPMediaItemPropertyArtist];
MPMediaQuery *myComplexQuery = [[MPMediaQuery alloc] init];
[myComplexQuery addFilterPredicate: artistNamePredicate];
NSInteger songCount = [[myComplexQuery collections] count]; //number of songs
myComplexQuery.groupingType = MPMediaGroupingAlbum;
NSInteger albumCount = [[myComplexQuery collections] count]; //number of albums

The artistsQuery convenience constructor does not sort and group by album. artistsQuery returns an array of media item collections of all artists sorted alphabetically by artist name. Nested inside each artist collection is an array of media items associated with all songs for that artist. The nested array is sorted alphabetically by song title.
One way to keep a count of albums by artist is to enumerate through all the song items for each artist collection and use a NSMutableSet to keep track of distinct album titles associated with each song. Then add the count of the set as the value for each artist key in a NSMutableDictionary. Any duplicate album titles will not be added since a NSMutableSet will only take distinct objects:
MPMediaQuery *artistQuery = [MPMediaQuery artistsQuery];
NSArray *songsByArtist = [artistQuery collections];
NSMutableDictionary *artistDictionary = [NSMutableDictionary dictionary];
NSMutableSet *tempSet = [NSMutableSet set];
[songsByArtist enumerateObjectsUsingBlock:^(MPMediaItemCollection *artistCollection, NSUInteger idx, BOOL *stop) {
NSString *artistName = [[artistCollection representativeItem] valueForProperty:MPMediaItemPropertyArtist];
[[artistCollection items] enumerateObjectsUsingBlock:^(MPMediaItem *songItem, NSUInteger idx, BOOL *stop) {
NSString *albumName = [songItem valueForProperty:MPMediaItemPropertyAlbumTitle];
[tempSet addObject:albumName];
}];
[artistDictionary setValue:[NSNumber numberWithUnsignedInteger:[tempSet count]]
forKey:artistName];
[tempSet removeAllObjects];
}];
NSLog(#"Artist Album Count Dictionary: %#", artistDictionary);
It would be cleaner if you change the query to albumsQuery. This query groups and sorts the collection by album name. Then it is just a matter of enumerating through the array of album collections and keeping a count of the representative artist name for each album in a NSCountedSet. The counted set will track the number of times objects are inserted:
MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery];
NSArray *albumCollection = [albumQuery collections];
NSCountedSet *artistAlbumCounter = [NSCountedSet set];
[albumCollection enumerateObjectsUsingBlock:^(MPMediaItemCollection *album, NSUInteger idx, BOOL *stop) {
NSString *artistName = [[album representativeItem] valueForProperty:MPMediaItemPropertyArtist];
[artistAlbumCounter addObject:artistName];
}];
NSLog(#"Artist Album Counted Set: %#", artistAlbumCounter);
You can also retrieve the count for a given object in a NSCountedSet with the countForObject: method.

Swift 2 translation of Bryan's answer:
var artistQuery = MPMediaQuery.artistsQuery()
var artistQuery.groupingType = MPMediaGrouping.AlbumArtist
var songsByArtist = artistQuery.collections
var artistDictionary = NSMutableDictionary()
var tempSet = NSMutableSet()
songsByArtist.enumerateObjectsUsingBlock { (artistCollection, idx, stop) -> Void in
let collection = artistCollection as! MPMediaItemCollection
let rowItem = collection.representativeItem
let artistName = rowItem?.valueForProperty(MPMediaItemPropertyAlbumArtist)
let collectionContent:NSArray = collection.items
collectionContent.enumerateObjectsUsingBlock { (songItem, idx, stop) -> Void in
let item = songItem as! MPMediaItem
let albumName = item.valueForProperty(MPMediaItemPropertyAlbumTitle)
self.tempSet.addObject(albumName!)
}
self.artistDictionary.setValue(NSNumber(unsignedInteger: self.tempSet.count), forKey: artistName! as! String)
self.tempSet.removeAllObjects()
}
print("Album Count Dictionary: \(artistDictionary)")

albumsQuery us this if it helps u or see more on this link below
http://developer.apple.com/library/ios/#documentation/mediaplayer/reference/MPMediaQuery_ClassReference/Reference/Reference.html

Thanks Tim E, I couldn't get your code to work first time, but I modded it to this and it works now.
let artistQuery = MPMediaQuery.artistsQuery()
artistQuery.groupingType = MPMediaGrouping.AlbumArtist
let songsByArtist = artistQuery.collections! as NSArray
let artistDictionary = NSMutableDictionary()
let tempSet = NSMutableSet()
songsByArtist.enumerateObjectsUsingBlock( { (artistCollection, idx, stop) -> Void in
let collection = artistCollection as! MPMediaItemCollection
let rowItem = collection.representativeItem
let artistName = rowItem?.valueForProperty(MPMediaItemPropertyAlbumArtist)
let collectionContent:NSArray = collection.items
collectionContent.enumerateObjectsUsingBlock({ (songItem, idx, stop) -> Void in
let item = songItem as! MPMediaItem
let albumName = item.valueForProperty(MPMediaItemPropertyAlbumTitle)
tempSet.addObject(albumName!)
})
artistDictionary.setValue(NSNumber(unsignedInteger: UInt(tempSet.count)), forKey: artistName! as! String)
tempSet.removeAllObjects()
})
print("Album Count Dictionary: \(artistDictionary)")

Sorry for late answer.
Posting my answer in case it is helpful to someone.
Below code gets all artists group by album artist and get its all Albums and songs inside the album.
/// Get all artists and their songs
///
func getAllArtists() {
let query: MPMediaQuery = MPMediaQuery.artists()
query.groupingType = .albumArtist
let artistsColelctions = query.collections
artists.removeAll()
var tempSet = NSMutableSet()
guard artistsColelctions != nil else {
return
}
// 1. Create Artist Objects from collection
for collection in artistsColelctions! {
let item: MPMediaItem? = collection.representativeItem
var artistName = item?.value(forKey: MPMediaItemPropertyArtist) as? String ?? "<Unknown>"
artistName = artistName.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines)
let artistId = item!.value(forProperty: MPMediaItemPropertyArtistPersistentID) as! NSNumber
// temp
let albumName = item?.albumTitle
let albumID = item?.albumPersistentID
print(albumName)
print(albumID)
// Create artist item
let artist = Artist()
artist.name = artistName
artist.artworkTitle = String(artistName.characters.prefix(1)).uppercased()
artist.artistId = String(describing: artistId)
// 2. Get Albums for respective Artist object
//--------------------------------------------
let mediaQuery2 = MPMediaQuery.albums()
let predicate2 = MPMediaPropertyPredicate.init(value: artistId, forProperty: MPMediaItemPropertyArtistPersistentID)
mediaQuery2.addFilterPredicate(predicate2)
let albums = mediaQuery2.collections
for collection in albums! {
let item: MPMediaItem? = collection.representativeItem
let albumName = item?.value(forKey: MPMediaItemPropertyAlbumTitle) as? String ?? "<Unknown>"
let albumId = item!.value(forProperty: MPMediaItemPropertyAlbumPersistentID) as! NSNumber
let artistName = item?.value(forKey: MPMediaItemPropertyAlbumArtist) as? String ?? "unknown"
let genreName = item?.genre ?? ""
let year = item?.releaseDate ?? item?.dateAdded
let dateAdded = item?.dateAdded
// Create album object
let album = Album()
album.name = albumName
album.artistName = artistName
album.genre = genreName
album.releaseDate = year
album.dateAdded = dateAdded
album.albumId = String(describing: albumId)
// Add artwork to album object
let artwork = Artwork.init(forAlbum: item)
album.artwork = artwork
// 3. Get Songs inside the resepctive Album object
//---------------------------------------------------
let mediaQuery = MPMediaQuery.songs()
let predicate = MPMediaPropertyPredicate.init(value: albumId, forProperty: MPMediaItemPropertyAlbumPersistentID)
mediaQuery.addFilterPredicate(predicate)
let song = mediaQuery.items
if let allSongs = song {
var index = 0
for item in allSongs {
let pathURL: URL? = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
let isCloud = item.value(forProperty: MPMediaItemPropertyIsCloudItem) as! Bool
let trackInfo = TrackInfo()
trackInfo.index = index
trackInfo.mediaItem = item
trackInfo.isCloudItem = isCloud
trackInfo.isExplicitItem = item.isExplicitItem
trackInfo.isSelected = false
trackInfo.songURL = pathURL
album.songs?.append(trackInfo)
index += 1
}
}
artist.albums?.append(album)
}
// Finally add the artist object to Artists Array
artists.append(artist)
}
}

Related

How to append the contents of NSSet to [NSManagedObject]?

My Code
isFiltering = true
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let words = textInSearchField.components(separatedBy: " ")
for word in words{
if (word).count == 0{
continue
}
let firstNamePredicate = NSPredicate(format: "firstName contains[c] %#", word)
let lastNamePredicate = NSPredicate(format: "lastName contains[c] %#", word)
let idPredicate = NSPredicate(format: "id contains[c] %#", word)
let orPredicate = NSCompoundPredicate(type: NSCompoundPredicate.LogicalType.or, subpredicates: [firstNamePredicate, lastNamePredicate, idPredicate])
clientsEntity.predicate = orPredicate
clientResults = try! context.fetch(clientsEntity) as! [NSManagedObject]
let sort:NSSortDescriptor = NSSortDescriptor(key:"dateSorted", ascending: false)
for (index, ob) in clientResults.enumerated(){
let relationship = ob.value(forKey: "assessed_by") as! NSSet
let array = relationship.sortedArray(using: [sort]) as! [NSManagedObject]
for item in array.enumerated() {
results.append(item.element)
print(results)
}
}
My data model:
I am using a tableView to display my data which works great, now I have implemented a filter function which allows the user to search based on a Clients first name, last name, id etc using NSCompoundPredicate.
I then sort the resulting [NSManagedObject] by date using NSSortDescriptor, my aim is to set my clientResults variable to contain the SORTED contents of the NSSet. My print statement only outputs that there is one Assessment inside the results variable when in actual fact the NSSet contains two of these NSManagedObjects.
let sort:NSSortDescriptor = NSSortDescriptor(key:"dateSorted", ascending: false)
for (index, ob) in clientResults.enumerated(){
let relationship = ob.value(forKey: "assessed_by") as! NSSet
let array = relationship.sortedArray(using: [sort]) as! [NSManagedObject]
// MARK - I enumerate the contents of the sorted array.
for item in array.enumerated() {
results.append(item.element)
print(results)
}
}
What is the best practice for assigning the contents of the NSSet to a variable of type [NSManagedObject]?
Thank you.
If you know that elements in NSSet are of type NSManagedObject why not just do
let managedObjectsArray = set.allObjects
or if you want to make sure it is of correct type you can do:
if let managedObjectsArray = set.allObjects as? [NSManagedObject] {
//do what you want with [NSManagedObject] array
}

How to convert datas in Swift from NSArray to Dictionary?

I need convert NSArray to Dictionary, but don't know how can I do it.
After fetch request I have result in NSArray. This my request:
var results: NSArray = []
func fetchUpdateAttendee() {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Attendee")
let predicate = NSPredicate(format: "needUpdate != false")
fetchRequest.predicate = predicate
results = try! DBWorker.context.fetch(fetchRequest) as NSArray
results.forEach { result in
print(result)
}
var results: NSArray = []
print(results) give me
<Attendee: 0x60c000097750> (entity: Attendee; id: 0x60c000422200 <x-coredata:///Attendee/t9E88E2EE-9258-4FAE-AF80-9B036838C6D631> ; data: {
address = "1411 E 31st St";
affiliation = "";
attendeeType = nil;
city = Aguanga;
degree = MD;
email = "";
fax = "";
firstName = Oliver1212;
fullStateLicense = "";
id = nil;
lastName = Aalami;
meeting = nil;
needUpdate = 1;
phone = "";
signature = nil;
signatureTimeStamp = nil;
specialty = Surgery;
state = CA;
stateLicense = "";
status = nil;
timeStamp = nil;
zip = 92536;
})
I need to put these datas to: let dic4Attendee: [String: Any] = [:]
This is a Core Data NSManagedObject subclass, so the code is supposed to be
var results = [Attendee]()
func fetchUpdateAttendee() {
let fetchRequest = NSFetchRequest<Attendee>(entityName: "Attendee")
let predicate = NSPredicate(format: "needUpdate == TRUE")
fetchRequest.predicate = predicate
do {
results = try DBWorker.context.fetch(fetchRequest)
results.forEach { result in
print(result)
}
} catch { print(error) }
}
never use NSArray to represent an array of NSManagedObject subclass.
Add a property dictionaryRepresentation in the Attendee class and return the key value pairs you need for example
var dictionaryRepresentation : [String:Any] {
return ["address" : address,
"affiliation" : affiliation,
"city" : city
// and so on
]
}
then map the array
let mappedArray = results.map{ $0.dictionaryRepresentation }

Apply sortDescriptor to NSFetchRequest after grouping

I am building a Showroom that has 45000+ catalogs each represented as Literature in CoreData.
To optimise search - instead of searching over Literature, I've added another entity - LiteratureSearchIndex in CoreData, that has searchvalue (either title word/keyword/tag etc. that represents part of the Literature), score (to weight the results) and literatureid.
When the user types something in the search or filters - I do a fetchRequest on the LiteratureSearchIndex, group the results and sort them on the total score.
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "LiteratureSearchIndex")
let entityDescription = NSEntityDescription.entity(forEntityName: "LiteratureSearchIndex", in: moc)
let sumDescription = NSExpressionDescription()
sumDescription.name = "totalScore"
sumDescription.expression = NSExpression(forFunction: "sum:", arguments: [NSExpression(forKeyPath: "score")])
sumDescription.expressionResultType = .integer64AttributeType
if let literatureid = entityDescription?.attributesByName["literatureid"] {
request.propertiesToGroupBy = [literatureid]
request.propertiesToFetch = [literatureid, sumDescription]
request.havingPredicate = NSPredicate(format: "%# >= %#", NSExpression(forVariable: "totalScore"), NSNumber(value: minScore))
}
request.predicate = ...
request.resultType = .dictionaryResultType
request.fetchBatchSize = 50
I'm currently sorting the results after the moc.fetch() like this:
results.sort(by: { (a: [String: Any], b: [String: Any]) -> Bool in
if let aTotalScore = a["totalScore"] as? Int, let bTotalScore = b["totalScore"] as? Int {
return aTotalScore > bTotalScore
} else {
return true
}
})
Is there a better/faster way to sort the results (similar to applying havingPredicate over the array of dictionaries)?

Issue: Saving Json data to Core Data

I am trying to learn how to read data from a blog and save it to core data but the save is not working as intended. There are 4 blog entries and I expect to have 4 different entries in core data. Please see the code below and let me know where i went wrong:
let task = session.dataTaskWithURL(url!, completionHandler:{(data , response, error) -> Void in
if (error != nil){
println(error)
}else{
var jsonResult:NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as NSDictionary
var managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext!
let newBlog = NSEntityDescription.insertNewObjectForEntityForName("BlogDetails",inManagedObjectContext:managedObjectContext) as NSManagedObject
var dateFormater = NSDateFormatter()
dateFormater.dateFormat = "yyyy-MM-dd HH:mm:ss" //"yyyy-MM-dd"
var readRequest = NSFetchRequest(entityName: "BlogDetails")
for var i = 0; i < ((jsonResult["items"] as? NSArray)?.count)!; i++ {
var item = jsonResult["items"]![i] as NSDictionary
var blogAuthorDirectory = item["author"]! as NSDictionary
var blogAuthor = blogAuthorDirectory["displayName"] as NSString
var blogAuthorImageDirectory = blogAuthorDirectory["image"] as NSDictionary
// concatenate String
var blogAuthorImage = blogAuthorImageDirectory["url"] as NSString
var blogAuthorImageUrl = ("https:" + blogAuthorImage)
var title = item["title"] as String
// convert date from String
var publishedDate:NSDate = dateFormater.dateFromString(stringTmp as NSString)!
// read content
var content = item["content"] as? NSString
// Write it to core data
newBlog.setValue(blogAuthorImageUrl, forKey: "image")
newBlog.setValue(blogAuthor, forKey: "author")
newBlog.setValue(title, forKey: "title")
newBlog.setValue(publishedDate, forKey: "publisheddate")
managedObjectContext.save(nil)
var results = managedObjectContext.executeFetchRequest(readRequest, error: nil)
println(results)
}
}
})
task.resume()
following are the entries in result in the last iteration:
1. It only has 3 dictionary counts out of which values in first 2 count has all items as nil. how is that being generated?
2. With every iteration, it overwrites value in last count and doesn't append it.
Thanks for your help.
If you want to append objects to your CoreData, you need to do insertIntoManagedObjectContext before you call the managedContext.save(nil) method.
However, your
let newBlog = NSEntityDescription.insertNewObjectForEntityForName("BlogDetails",inManagedObjectContext:managedObjectContext) as NSManagedObject
is declared outside of your for loop, so probably no new blog created after each iteration.

iPhone get all albums/artists

does any of you have example code (or a link to it) of how to retrieve all music albums or artist from the iPod media library?
Thanks in advance!
Use a MPMediaQuery:
MPMediaQuery *allAlbumsQuery = [MPMediaQuery albumsQuery];
NSArray *allAlbumsArray = [allAlbumsQuery collections];
The allItems array does now contain MPMediaItemCollections, grouping is done by album. Now you can walk through the arrays.
for (MPMediaItemCollection *collection in allAlbumsArray) {
MPMediaItem *item = [collection representativeItem];
}
Thanks for the answer, here is working sample code that prints out the albums and artists in case someone needs it:
NSMutableString *outText = [[NSMutableString alloc] initWithString:#"Albums:"];
[outText appendFormat:#"\r\n count:%i",[[[MPMediaQuery albumsQuery] collections] count]];
for (MPMediaItemCollection *collection in [[MPMediaQuery albumsQuery] collections]) {
[outText appendFormat:#"\r\n -%#",[[collection representativeItem] valueForProperty:MPMediaItemPropertyAlbumTitle]];
}
[outText appendString:#"\r\n\r\n Artist:"];
for (MPMediaItemCollection *collection in [[MPMediaQuery artistsQuery] collections]) {
[outText appendFormat:#"\r\n -%#",[[collection representativeItem] valueForProperty:MPMediaItemPropertyArtist]];
}
NSLog(#"%#",[outText autorelease]);
Here you go. You can get the albums and their songs.
/// Get all albums and their songs
///
func getAllAlbums() {
let query: MPMediaQuery = MPMediaQuery.albums()
let allAlbums = query.collections
allAlbumItems?.removeAll()
guard allAlbums != nil else {
return
}
for collection in allAlbums! {
let item: MPMediaItem? = collection.representativeItem
let albumName = item?.value(forKey: MPMediaItemPropertyAlbumTitle) as? String ?? "<Unknown>"
let albumId = item!.value(forProperty: MPMediaItemPropertyAlbumPersistentID) as! NSNumber
let artistName = item?.value(forKey: MPMediaItemPropertyArtist) as? String ?? "<Unknown>"
let album = Album()
album.name = albumName
album.artistName = artistName
album.albumId = String(describing: albumId)
print("Album name: \(albumName)")
// Get all songs in this album
let mediaQuery = MPMediaQuery.songs()
let predicate = MPMediaPropertyPredicate.init(value: albumId, forProperty: MPMediaItemPropertyAlbumPersistentID)
mediaQuery.addFilterPredicate(predicate)
let song = mediaQuery.items
if let allSongs = song {
var index = 0
for item in allSongs {
let pathURL: URL? = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
if pathURL == nil {
print("#Warning!!! Track : \(item) is not playable.")
} else {
let trackInfo = SongItem()
trackInfo.index = index
trackInfo.mediaItem = item
let title = item.value(forProperty: MPMediaItemPropertyTitle) as? String ?? "<Unknown>"
let artistName = item.value(forProperty: MPMediaItemPropertyArtist) as? String ?? "<Unknown>"
trackInfo.songName = title
trackInfo.artistName = artistName
trackInfo.isSelected = false
trackInfo.songURL = item.value(forProperty: MPMediaItemPropertyAssetURL) as? URL
album.songs?.append(trackInfo)
index += 1
}
}
}
// Finally add the album object to albums array
allAlbumItems?.append(album)
}
print("Total Album count: \(allAlbumItems?.count)")
}