Hello i am trying to retrieve Artist with MPMediaQuery in iOS with following code.
In My ViewDidLenter code hereoad
MPMediaQuery *query = [MPMediaQuery artistsQuery];
self.arrayOfArtist = [query collections];
And In my cellForRowAtIndexPath
cell.textLabel.text = [NSString stringWithFormat:#"%#",[[self.arrayOfArtist objectAtIndex:indexPath.row] valueForProperty:MPMediaItemPropertyArtist]];
When i check with NSLog, my arrayOfArtist count is about 330.
However in my UITableView , it's only show NULL.
Is there anything i am wronging?
you should write:
cell.textLabel.text = [NSString stringWithFormat:#"%#",[[[self.arrayOfArtist objectAtIndex:indexPath.row] representativeItem] valueForProperty:MPMediaItemPropertyArtist]];
You can use the code to retrieve the artists and their songs.
/// Get all artists and their songs
///
func getAllArtists() {
let query: MPMediaQuery = MPMediaQuery.artists()
let allArtists = query.collections
allArtistItems?.removeAll()
guard allArtists != nil else {
return
}
for collection in allArtists! {
let item: MPMediaItem? = collection.representativeItem
let artistName = item?.value(forKey: MPMediaItemPropertyArtist) as? String ?? "<Unknown>"
let artistId = item!.value(forProperty: MPMediaItemPropertyArtistPersistentID) as! NSNumber
let artist = Artist()
artist.name = artistName
artist.artistId = String(describing: artistId)
print("Artist name: \(artistName)")
// Get all songs of this Artist
let mediaQuery = MPMediaQuery.songs()
let predicate = MPMediaPropertyPredicate.init(value: artistId, forProperty: MPMediaItemPropertyArtistPersistentID)
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
artist.songs?.append(trackInfo)
index += 1
}
}
}
// Finally add the album object to albums array
allArtistItems?.append(artist)
}
print("Total Artist count: \(allArtistItems?.count)")
}
You need to grab the artist property and then save it in the array. The valueForProperty method does not work correctly if your trying to use it on a standard array.
Related
Currently I am checking whether the object already exists in core data based on id and then updating and inserting. Is there any better way to do it? Have added "id" as a unique constraint, Which prevents inserting of objects with same "id". Does inserting just update the existing object with same id?
#nonobjc public class func saveUserMovies(movieJSON: [[String: Any]], user: UserProfile, isFavorites: Bool = false, isWatchlisted: Bool = false) {
let context = MMPersistentStore.sharedInstance.privateManagedObjectContext
for movie in movieJSON {
let movieID = movie["id"] as! Int
let fetchMovieWithIDRequest = fetchMovieRequest()
let moviePredicate = NSPredicate(format: "id == %d", movieID)
let sortDiscriptor = NSSortDescriptor(key: "id", ascending: false)
fetchMovieWithIDRequest.sortDescriptors = [sortDiscriptor]
fetchMovieWithIDRequest.predicate = moviePredicate
var userMovie: UserMovie?
context.performAndWait {
do {
userMovie = try fetchMovieWithIDRequest.execute().first
} catch {
print(MMErrorStrings.coreDataFetchError)
}
}
if let fetchedMovie = userMovie {
fetchedMovie.genreIds = movie["genre_ids"] as? [Int64]
fetchedMovie.adult = movie["adult"] as? Bool ?? false
if isFavorites {
fetchedMovie.isFavorite = isFavorites
} else {
fetchedMovie.isWatchlisted = isWatchlisted
}
fetchedMovie.video = movie["video"] as? Bool ?? false
fetchedMovie.backdropPath = movie["backdrop_path"] as? String
fetchedMovie.originalLanguage = movie["original_language"] as? String
fetchedMovie.originalTitle = movie["original_title"] as? String
fetchedMovie.overview = movie["overview"] as? String
fetchedMovie.posterPath = movie["poster_path"] as? String
fetchedMovie.releaseDate = movie["release_date"] as? String
fetchedMovie.releaseYear = String(fetchedMovie.releaseDate?.prefix(4) ?? "")
fetchedMovie.title = movie["title"] as? String
fetchedMovie.popularity = movie["popularity"] as? Double ?? 0.0
fetchedMovie.voteCount = movie["voteCount"] as? Int64 ?? 0
fetchedMovie.voteAverage = movie["voteAverage"] as? Double ?? 0.0
MMPersistentStore.sharedInstance.save(context: context)
} else {
let fetchedMovie = UserMovie(context: context)
fetchedMovie.id = movie["id"] as? Int64 ?? 0
fetchedMovie.user = user
fetchedMovie.genreIds = movie["genre_ids"] as? [Int64]
fetchedMovie.adult = movie["adult"] as? Bool ?? false
if isFavorites {
fetchedMovie.isFavorite = isFavorites
} else {
fetchedMovie.isWatchlisted = isWatchlisted
}
fetchedMovie.video = movie["video"] as? Bool ?? false
fetchedMovie.backdropPath = movie["backdrop_path"] as? String
fetchedMovie.originalLanguage = movie["original_language"] as? String
fetchedMovie.originalTitle = movie["original_title"] as? String
fetchedMovie.overview = movie["overview"] as? String
fetchedMovie.posterPath = movie["poster_path"] as? String
fetchedMovie.releaseDate = movie["release_date"] as? String
fetchedMovie.releaseYear = String(fetchedMovie.releaseDate?.prefix(4) ?? "")
fetchedMovie.title = movie["title"] as? String
fetchedMovie.popularity = movie["popularity"] as? Double ?? 0.0
fetchedMovie.voteCount = movie["voteCount"] as? Int64 ?? 0
fetchedMovie.voteAverage = movie["voteAverage"] as? Double ?? 0.0
MMPersistentStore.sharedInstance.save(context: context)
}
}
}
}
Have added "id" as a unique constraint, Which prevents inserting of objects with same "id".
I didn't use it before yet
Does inserting just update the existing object with same id?
No, it'll insert the new object.
For your case, you could make a refactoring, please refer the findOrCreate function in https://github.com/objcio/core-data/blob/master/SharedCode/Managed.swift
It'll help you avoid duplicated code.
One more thing, your request doesn't need the sortDescriptor, and it should have the limit = 1, and returnObjectAsFaults = false for optimisation.
After that, you just need to make sure your function is called in the same context to avoid duplications.
In my project I have one NSMutableArray and NSArray named logOrderMutArray and openOrderArray, here I want to merge the openOrderArray with logOrderMutArray based on same "AccountNumber". If both array have same "AccountNumber" means I want to replace the particular index in logOrderMutArray with openOrderArray index else if "AccountNumber" was not same in some index means I want to add the openOrderArray to logOrderMutArray.
But I have made some mistakes. How can I solve the issue?
Here is my code:
func mergeOrderAndOpenOrderArrayData() {
let validateMutArray = NSMutableArray()
for i in 0..<self.logOrderMutArray.count {
var dictMutableCopy = NSDictionary()
var editDict = NSMutableDictionary()
for j in 0..<self.openOrderArray.count {
var newDict = NSDictionary()
if let logOrderfips:NSNumber = (logOrderMutArray[i] as AnyObject).value(forKey: "FIPS") as? NSNumber {
if let openOrderfips:NSNumber = (openOrderArray[j] as AnyObject).value(forKey: "FIPS") as? NSNumber {
if let logOrderAccNum:String = (logOrderMutArray[i] as AnyObject).value(forKey: "AccountNumber") as? String {
if let openOrderAccNum:String = (openOrderArray[j] as AnyObject).value(forKey: "AccountNumber") as? String {
if logOrderfips == openOrderfips && logOrderAccNum == openOrderAccNum {
dictMutableCopy = logOrderMutArray[i] as! NSDictionary
editDict = NSMutableDictionary(dictionary: dictMutableCopy)
newDict = openOrderArray[j] as! NSDictionary
editDict["newlyAdded"] = newDict
logOrderMutArray.replaceObject(at: i, with: editDict)
}
else {
logOrderMutArray.add(openOrderArray[j])
}
}
}
}
}
}
}
}
I am using following to code to arrange the array of contacts in sections (for e.g. contact with prefix "A" should show under "A" section). If there are contacts 4-5 hundred then it takes 20 sec to process.
Can you please check what is issue here? or any alternate for this.
let finalArr = NSMutableArray()
for contactDetail in conatctsArr {
let name = (contactDetail as! Dictionary)["givenName"] ?? ""// Getting First character of name
var key = String()
if name.characters.count > 0 {
let index1 = name.index((name.startIndex), offsetBy: 1)
key = name.substring(to: index1)
}
else {
key = ""
}
// getting all contatcts starting with particular character
let predicate=NSPredicate(format: "SELF.givenName beginswith[c] %#",key)
let filteredArr = (conatctsArr as NSArray).filtered(using: predicate)
var dic = Dictionary<String, Any>()
dic["key"] = key
dic["values"] = filteredArr
if filteredArr.count > 0 && !(finalArr.contains(dic)) {
finalArr.add(dic)
}
}
Removing the filtered elements from the array after processing in each loop might improve the performance. Try:
let finalArr = NSMutableArray()
var processingArray = NSMutableArray(array:conatctsArr)
while processingArray.count > 0 {
let contactDetail = processingArray[0]
let name = (contactDetail as! Dictionary)["givenName"] ?? ""
var key = String()
if name.characters.count > 0 {
let index1 = name.index((name.startIndex), offsetBy: 1)
key = name.substring(to: index1)
}
else {
key = ""
}
let predicate=NSPredicate(format: "SELF.givenName beginswith[c] %#",key)
let filteredArr = processingArray.filtered(using: predicate)
if filteredArr.count > 0 {
var dic = Dictionary<String, Any>()
dic["key"] = key
dic["values"] = filteredArr
finalArr.add(dic)
}
processingArray.removeObjects(in: filteredArr)
}
In your code, the filtering is being done multiple times for the same key. Try :
let finalArr = NSMutableArray()
for contactDetail in conatctsArr
{
let keysArray = [“A”, “B”, “C”,…. “Z”]
for key in keysArray
{
let predicate=NSPredicate(format: "SELF.givenName beginswith[c] %#",key)
let filteredArr = (conatctsArr as NSArray).filtered(using: predicate)
var dic = Dictionary<String, Any>()
dic["key"] = key
dic["values"] = filteredArr
if filteredArr.count > 0) {
finalArr.add(dic)
}
}
}
{
QueueId = 27;
SongId = 38;
artWorkURL = "<null>";
duration = 58258;
"stream_url" = "https://api.soundcloud.com/tracks/233301835/stream";
title = Magenta;
trackID = 233301835;
userAvatar = "https://i1.sndcdn.com/avatars-000188204071-llusgk-large.jpg";
userName = Agiv;
},
{
QueueId = 27;
SongId = 39;
artWorkURL = "<null>";
duration = 79000;
"stream_url" = "https://api.soundcloud.com/tracks/233301833/stream";
title = Nino;
trackID = 233301833;
userAvatar = "https://i1.sndcdn.com/avatars-000157591669-eva3mg-large.jpg";
userName = "SWR Umwelt und Ern\U00e4hrung";
}
My array of dictionary format is as above and multiple tracks i want to check 27 is already there or not ?
You can do this with the filter function
let queueID27Exists = !array.filter({$0["QueueId"] as? Int == 27}).isEmpty
This answer is for your previous JSON object!
if let results : NSDictionary = post.objectForKey("data") as? NSDictionary {
let array:NSArray = (results.valueForKey("track") as! NSArray)
if "25" == array[0].valueForKey("trackID") as? String {
NSLog("YES")
} else {
NSLog("NO")
}
}
var found = false
for (_, data) in post {
let track = data["track"]
if track["trackID"] == "25" {
found = true
}
I'm getting JSON data from an API and parsing that data in objects, which are then simply stored in an array of objects. The objects themselves contain data about articles from a newspaper. However, I need to filter that data. Some of the objects I'm getting from my JSON actually have no article content because they are pictures and not articles (i.e. some of the "nodes" from the API's JSON have content that I don't want to see in my table view).
In my JSON-parsing function, I've tried to make it so that the parsed object will only get added to the array of parsed objects if the character count of the "articleContent" variable is above 40. Here is what it looked like.
if issueElement.articleContent.characters.count > 40 {
self.currentIssueObjects.addObject(issueElement)
}
However, this simply does not work. I get the typical "unexpectedly found nil while unwrapping an Optional value" error message (I don't get a specific line for the error). How can I make this work ? I'm essentially trying to prevent the array from having objects with empty articleContent, because then that screws up my table view (empty cells, duplicates, etc...).
Here is my cellForRowAtIndexPath code, and my JSON-parsing code:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell {
let row = indexPath.row
guard let cell = tableView.dequeueReusableCellWithIdentifier(CurrentIssueArticlesTableCellIdentifier, forIndexPath: indexPath) as? CurrentIssueArticlesTableViewCell else {
print ("error: currentIssueTableView cell is not of class CurrentIssueArticlesTableViewCell, we will use EditorialsTableViewCell instead")
return tableView.dequeueReusableCellWithIdentifier(CurrentIssueArticlesTableCellIdentifier, forIndexPath: indexPath) as! EditorialsTableViewCell
}
let currentIssueObject = currentIssueObjects.objectAtIndex(indexPath.row) as! IssueElement
let title = currentIssueObject.title ?? ""
let timeStampDateObject = NSDate(timeIntervalSince1970: NSTimeInterval(currentIssueObject.timeStamp))
let timeStampDateString = dateFormatter.stringFromDate(timeStampDateObject) ?? "Date unknown"
if let author = currentIssueObject.author {
cell.currentIssueArticlesAuthorLabel!.font = UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
cell.currentIssueArticlesAuthorLabel!.text = author
}
let issueNumber = currentIssueObject.issueNumber ?? ""
let volumeNumber = currentIssueObject.volumeNumber ?? ""
let articleContent = currentIssueObject.articleContent ?? ""
let nodeID = currentIssueObject.nodeID ?? 0
cell.currentIssueArticlesHeadlineLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cell.currentIssueArticlesHeadlineLabel.text = title
cell.currentIssueArticlesPublishDateLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
cell.currentIssueArticlesPublishDateLabel.text = timeStampDateString
if row == 0 {
cell.userInteractionEnabled = false
let imageURL = (currentIssueObjects.objectAtIndex(row) as! IssueElement).imageURL
cell.currentIssueArticlesHeadlineLabel.textColor = UIColor.clearColor()
cell.currentIssueArticlesAuthorLabel.textColor = UIColor.clearColor()
cell.currentIssueArticlesPublishDateLabel.textColor = UIColor.clearColor()
cell.request?.cancel()
if let image = self.imageCache.objectForKey(imageURL!) as? UIImage {
cell.currentIssueArticlesBackgroundImageView.image = image
} else {
cell.currentIssueArticlesBackgroundImageView.image = UIImage(named: "reveal Image")
cell.request = Alamofire.request(.GET, imageURL!).responseImage() { response in
if response.result.error == nil && response.result.value != nil {
self.imageCache.setObject(response.result.value!, forKey: response.request!.URLString)
cell.currentIssueArticlesBackgroundImageView.image = response.result.value
} else {
}
}
}
cell.currentIssueArticlesBackgroundImageView.hidden = false
}
else {
cell.currentIssueArticlesBackgroundImageView.hidden = true
}
return cell
}
JSON-parsing code:
func populateCurrentIssue() {
if populatingCurrentIssue {
return
}
populatingCurrentIssue = true
self.cellLoadingIndicator.backgroundColor = goldenWordsYellow
self.cellLoadingIndicator.startAnimating()
Alamofire.request(GWNetworking.Router.Issue).responseJSON() { response in
if let JSON = response.result.value {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
var nodeIDArray : [Int]
if (JSON .isKindOfClass(NSDictionary)) {
for node in JSON as! Dictionary<String, AnyObject> {
let nodeIDValue = node.0
var lastItem : Int = 0
self.nodeIDArray.addObject(nodeIDValue)
if let issueElement : IssueElement = IssueElement(title: "Could not retrieve title", nodeID: 0, timeStamp: 0, imageURL: "init", author: "Author not found", issueNumber: "Issue # error", volumeNumber: "Volume # error", articleContent: "Could not retrieve article content", coverImageInteger: "init", coverImage: UIImage()) {
issueElement.title = node.1["title"] as! String
issueElement.nodeID = Int(nodeIDValue)!
let timeStampString = node.1["revision_timestamp"] as! String
issueElement.timeStamp = Int(timeStampString)!
issueElement.imageURL = String(node.1["image_url"])
if let author = node.1["author"] as? String {
issueElement.author = author
}
if let issueNumber = node.1["issue_int"] as? String {
issueElement.issueNumber = issueNumber
}
if let volumeNumber = node.1["volume_int"] as? String {
issueElement.volumeNumber = volumeNumber
}
if let articleContent = node.1["html_content"] as? String {
issueElement.articleContent = articleContent
}
issueElement.coverImageInteger = String(node.1["cover_image"]) // addition specific to the Current Issue View Controller
lastItem = self.currentIssueObjects.count
print(issueElement.nodeID)
if issueElement.articleContent.characters.count > 40 {
self.currentIssueObjects.addObject(issueElement)
print(issueElement.nodeID)
}
// Sorting with decreasing timestamp from top to bottom.
let timestampSortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)
self.currentIssueObjects.sortUsingDescriptors([timestampSortDescriptor])
// Placing the object with coverImage
let coverImageSortDescriptor = NSSortDescriptor(key: "coverImageInteger", ascending: false)
self.currentIssueObjects.sortUsingDescriptors([coverImageSortDescriptor])
let indexPaths = (lastItem..<self.currentIssueObjects.count).map {
NSIndexPath(forItem: $0, inSection: 0) }
}
}
}
dispatch_async(dispatch_get_main_queue()) {
self.currentIssueTableView.reloadData()
self.cellLoadingIndicator.stopAnimating()
self.cellLoadingIndicator.hidesWhenStopped = true
}
}
}
self.populatingCurrentIssue = false
}
}