This question already has an answer here:
Why is the title being repeated?
(1 answer)
Closed 7 years ago.
I am trying to add the song title and artist to a database every time the song changes. The program currently does that, but for some reason when I go to the next song, the same song is added more than once in the database, i.e. Song 1 is added 6 times in parse. Why is this happening and how can I fix it?
func applicationDidEnterBackground(application: UIApplication) {
print("entered background")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "getNowPlayingItem", name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: nil)
musicPlayer.beginGeneratingPlaybackNotifications()
backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({ () -> Void in
UIApplication.sharedApplication().endBackgroundTask(self.backgroundTask!)
self.backgroundTask = UIBackgroundTaskInvalid
})
}
func getNowPlayingItem() {
if let nowPlaying = musicPlayer.nowPlayingItem {
let title = nowPlaying[MPMediaItemPropertyTitle]
let artisttest = nowPlaying[MPMediaItemPropertyTitle]
if let artist = nowPlaying[MPMediaItemPropertyArtist] {
let objectPointer = PFObject(className: "Pointer")
let object = PFObject(className: "MasterSongs")
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
}
if self.Parsearray.contains(title! as! String){
print("already in db")
}else{
objectPointer["title"] = title
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = artist
objectPointer.saveInBackground()
//parseClass.saveInBackgroundWithBlock{(success: Bool, error: NSError!) -> Void in
objectPointer.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if success == false {
print(error)
} else {
print("Posted succesfully")
}
})
}
})
}
if(artisttest == nil){
let objectPointer = PFObject(className: "Pointer")
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
}
if self.Parsearray.contains(title! as! String){
print("already in db")
}else{
objectPointer["title"] = title
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = "No artist found :("
objectPointer.saveInBackground()
}
})
}
}
}
func applicationWillEnterForeground(application: UIApplication) {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
One thing I notice is that you add an observer for MPMusicPlayerControllerNowPlayingItemDidChangeNotification every time applicationDidEnterBackground is called.
You didn't include the applicationDidEnterForeground function - but hopefully you are removing those observer there. If you don't remove the observers, it would cause the getNowPlayingItem selector to be called for each applicationDidEnterBackground. So 6 applicationDidEnterBackground = 6 getNowPlayingItem.
Related
I am very new to cloud code, and I am having trouble. I have already referred to this How to prevent duplicate entry on parse? and I am trying to do the same exact thing. I am trying to prevent a duplicate entry in my class. I keep getting the error in the cloud code "Song already exists" even though the class is empty.
This is the cloud code. I am confused on what the querySongs.equalTo("title", newEntrySong.get("title")); is doing. Is it looking for the actual word title? becuase I need it to find a specific title.
Parse.Cloud.beforeSave("Pointer", function(request, response) {
var newEntrySong = request.object;
var querySongs = new Parse.Query("Pointer");
querySongs.equalTo("title", newEntrySong.get("title"));
//querySongs.equalTo("description", newEntrySong.get("description"));
// this could be a sort of signature for your song, to make more unique (skipping spaces and new lines for example)
// querySongs.equalTo("md5Title", newEntrySong.get("md5Title"));
querySongs.first({
success: function(temp) {
response.error({errorCode:123,errorMsg:"Song already exist!"});
},
error: function(error) {
response.success();
}
});
});
Below is my code for finding duplicates in the class, but it doesn't work well. Do I need to replace the whole thing? or get rid of it? since I have the cloud code
func getNowPlayingItem() {
print("entered function")
NSNotificationCenter.defaultCenter().removeObserver(self)
if let nowPlaying = musicPlayer.nowPlayingItem {
let title = nowPlaying[MPMediaItemPropertyTitle] as? String
let artisttest = nowPlaying[MPMediaItemPropertyTitle]
if let artist = nowPlaying[MPMediaItemPropertyArtist] as? String{
let objectPointer = PFObject(className: "Pointer")
let object = PFObject(className: "MasterSongs")
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
}
if self.Parsearray.contains(title!){
print("already in db")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "getNowPlayingItem", name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: nil)
}else{
objectPointer["title"] = title
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = artist
print("about to save with artist")
objectPointer.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if(error != nil){
print(error)
}else{
NSNotificationCenter.defaultCenter().addObserver(self, selector: "getNowPlayingItem", name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: nil)
print("saved")
}
})
}
})
}else{
let objectPointer = PFObject(className: "Pointer")
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
// print(self.Parsearray)
}
if self.Parsearray.contains(title!){
print("already in db with no artist")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "getNowPlayingItem", name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: nil)
}else{
objectPointer["title"] = title
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = "No artist found :("
print("about to save")
objectPointer.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if(error != nil){
print(error)
}else{
NSNotificationCenter.defaultCenter().addObserver(self, selector: "getNowPlayingItem", name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: nil)
print("saved")
}
})
}
})
}
}
}
The OP's beforeSave function fails to check whether or not the query identified a duplicate. As written, it always reports a duplicate, but can be fixed simply...
var newEntrySong = request.object;
var querySongs = new Parse.Query("Pointer");
querySongs.equalTo("title", newEntrySong.get("title"));
// whatever else you want to do that defines a duplicate
querySongs.first().then(function(result) {
// this is the check the code was missing
if (result) {
response.error({errorCode:123,errorMsg:"Song already exist!"});
} else {
response.success();
}
}, function(error) {
response.error(error);
})
If you have this working to prevent duplicates, there's no need for the client code to do the same.
I have 4 functions containing Parse query.findObjectsInBackgroundWithBlock. These are being called to grab data and then populate the table view. Using dispatch groups.
Here is two examples of my parse querys
func getEventImages() {
print("getEventImages enter")
dispatch_group_enter(self.group)
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!, error: NSError!) -> Void in
// Initialize your array to contain all nil objects as
// placeholders for your images
if error == nil {
self.eventMainImageArray = [UIImage?](count: objects.count, repeatedValue: nil)
for i in 0...objects.count - 1 {
let object: AnyObject = objects[i]
let mainImage = object["mainImage"] as! PFFile
//dispatch_group_enter(self.group)
mainImage.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let mainImage = UIImage(data:imageData)
self.eventMainImageArray[i] = mainImage
print("getEventImages appended")
}
else {
print("error!!")
}
})
}
}
print("getEventImages leave")
dispatch_group_leave(self.group)
}
}
func getEventInfo() {
print("eventInfo enter")
dispatch_group_enter(group)
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
self.eventNameArray = [String?](count: objects.count, repeatedValue: nil)
self.eventInfoArray = [String?](count: objects.count, repeatedValue: nil)
self.eventDateArray = [NSDate?](count: objects.count, repeatedValue: nil)
self.eventTicketsArray = [String?](count: objects.count, repeatedValue: nil)
if error == nil {
for i in 0...objects.count - 1 {
let object: AnyObject = objects[i]
let eventName = object["eventName"] as! String
let eventInfo = object["eventInfo"] as! String
let eventDate = object["eventDate"] as! NSDate
let eventTicket = object["Tickets"] as! String
self.eventNameArray[i] = eventName
self.eventInfoArray[i] = eventInfo
self.eventDateArray[i] = eventDate
self.eventTicketsArray[i] = eventTicket
print("event info appended")
}
}
print("event info leave")
dispatch_group_leave(self.group)
}
}
And my dispatch_group_nofity
dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
print("Finished reloadDataFromServer()")
self.tableView.reloadData()
self.refreshControl?.finishingLoading()
}
}
The problem is that its hit and miss if the data gets retrieved quick enough before dispatch_group_leave(self.group) is called leading to reloading the tableview data too soon. I need to get this so the dispatch_group_leave gets called when the appending is completed.
There is no need for two methods to retrieve the data, no need to unpack the data into multiple arrays and no need to use dispatch groups.
All you need is a simple method to retrieve your event data
var events:[PFObject]=[PFObject]()
func getEventInfo() {
let query = PFQuery(className: "events")
query.orderByAscending("eventDate")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]!,error: NSError!) -> Void in
if error==nil {
self.events=objects as! [PFObject]
self.tableView.reloadData()
} else {
print("Something went wrong! - \(error)"
}
self.refreshControl?.finishingLoading()
}
}
Then, you haven't shown your cellForRowAtIndexPath but you would have something like
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! MyTableViewCell
let event=self.events[indexPath.row]
cell.eventName.text=event["eventName"] as? String
cell.eventInfo.text=event["eventInfo"] as? String
if let mainImageFile=event["mainImage"] as? PFFile {
mainImageFile.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
let mainImage = UIImage(data:imageData)
cell.mainImage= mainImage
}
else {
print("error!!")
}
}
return cell;
}
You can use a PFImageView or a framework like SDWebImage to handle image caching and putting a placeholder image in place while the image is loaded.
If you want to update an event is as easy as
var event=self.events[someindex];
event["eventName"]=newValue
event.saveInBackground()
I am trying to capture the data from a json page, and store it in a database. I can currently get the artist and title from the json page. I am filling a array in my program with the values captured from data to avoid repeats. When run the for loop and get the values and put them in the Parsearray, an infinite loop starts. It does not end, a it's always being called. I know this because i see "called" being printed in the console multiple times and it does not stop. How do I fix this?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated);
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
print(self.Parsearray)
print("called")
}
})
self.getSpotify()
}
func getSpotify(){
let searchTerm = "tgirish10"
var endpoint = NSURL(string: "<api URL>")
var data = NSData(contentsOfURL: endpoint!)
let task = NSURLSession.sharedSession().dataTaskWithURL(endpoint!) {(data, response, error) -> Void in
do {
if let jsonData = data,
let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as? NSDictionary,
let recent = dict["recenttracks"] as? NSDictionary,
let items = recent["track"] as? NSArray {
for item in items {
if let spotifytitle = item["name"] as? String {
print("title: \(spotifytitle)")
if let spotifyartist = item["artist"]!!["#text"] as? String{
print("artist: \(spotifyartist)")
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
print(self.Parsearray)
print("called")
}
if self.Parsearray.contains(spotifytitle){
print("already in db")
}else{
let objectPointer = PFObject(className: "Pointer")
objectPointer["title"] = spotifytitle
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = spotifyartist
objectPointer.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if(error != nil){
print(error)
}else{
print("saved")
}
})
}
})
}
}
}
}
} catch let jsonError as NSError {
print(jsonError)
}
}
task.resume()
}
Beyond the issues in the comments above, the main problem is that your code does the following:
get some data from Spotify
for each track, get all Pointer objects from Parse
for each of those objects, add title to parseArray (so after a while, the list will contain multiple copies of all titles)
then check if title of the track is in this list
So the loop does not seem to be infinite, but you are unnecessarily adding a lot of data.
Two options:
move the loading of the titles outside of the loop on tracks
or use a PFQuery which only returns Pointer objects which match, and just check if the returned list is empty or not
I have a program which will record the currently playing song, but I want the app to run in the background, so when the user is in a different app or when the device is locked and my app is in the background and they go to the next song, the program will know. Currently I can take the current playing song and put it into parse when the app is open.
func applicationDidEnterBackground(application: UIApplication) {
print("entered background")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "getNowPlayingItem", name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: nil)
musicPlayer.beginGeneratingPlaybackNotifications()
}
func getNowPlayingItem() {
if let nowPlaying = musicPlayer.nowPlayingItem {
let title = nowPlaying[MPMediaItemPropertyTitle] as? String
let artisttest = nowPlaying[MPMediaItemPropertyTitle]
if let artist = nowPlaying[MPMediaItemPropertyArtist] as? String{
let objectPointer = PFObject(className: "Pointer")
let object = PFObject(className: "MasterSongs")
// print("Artist: " + artist)
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
// print(self.Parsearray)
}
if self.Parsearray.contains(title!){
print("already in db")
}else{
objectPointer["title"] = title
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = artist
objectPointer.saveInBackground()
}
})
}else{
let object = PFObject(className: "MasterSongs")
object.setObject(title!, forKey: "title")
//object.setObject(artist!, forKey: "artist")
object.saveInBackground()
let objectPointer = PFObject(className: "Pointer")
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
// print(self.Parsearray)
}
if self.Parsearray.contains(title!){
print("already in db")
}else{
objectPointer["title"] = title
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = "No artist found :("
objectPointer.saveInBackground()
}
})
}
}
}
Apple support some of the background mode. If you are using any Apple framework for audio play then select your App target and go in Capabilites options and enable background mode and choose the appropriate options.
Check the screenshot.
I am getting the current playing song, and capturing the title and artist, and storing it in parse. For some reason, when the song plays, the program adds 4 or so of the same title/artist. I instead just want one. How do I fix this?
my objects as seen in the parse data browser
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated);
NSNotificationCenter.defaultCenter().addObserver(self, selector: "getNowPlayingItem", name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: nil)
musicPlayer.beginGeneratingPlaybackNotifications()
}
func getNowPlayingItem() {
NSNotificationCenter.defaultCenter().removeObserver(self)
if let nowPlaying = musicPlayer.nowPlayingItem {
let title = nowPlaying[MPMediaItemPropertyTitle] as? String
let artisttest = nowPlaying[MPMediaItemPropertyTitle]
if let artist = nowPlaying[MPMediaItemPropertyArtist] as? String{
let objectPointer = PFObject(className: "Pointer")
let object = PFObject(className: "MasterSongs")
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
// print(self.Parsearray)
}
if self.Parsearray.contains(title!){
print("already in db")
}else{
objectPointer["title"] = title
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = artist
objectPointer.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if(error != nil){
print(error)
}else{
NSNotificationCenter.defaultCenter().addObserver(self, selector: "getNowPlayingItem", name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: nil)
}
})
}
})
}else{
let objectPointer = PFObject(className: "Pointer")
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
// print(self.Parsearray)
}
if self.Parsearray.contains(title!){
print("already in db")
}else{
objectPointer["title"] = title
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = "No artist found :("
objectPointer.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if(error != nil){
print(error)
}else{
NSNotificationCenter.defaultCenter().addObserver(self, selector: "getNowPlayingItem", name: MPMusicPlayerControllerNowPlayingItemDidChangeNotification, object: nil)
}
})
}
})
}
}
}
From the evidence, there's a good chance that the function getNowPlayingItem is being called several times rapidly. It launches queries, a handful of which complete before anything is saved. Those query completions (with no saves done yet) launch a handful of saves and you get a handful of objects.
Check this by printing a message at the start of the method and just before saveInBackground paying attention to the timestamps on the console.
If I'm right, the fix is simple: (a) find out why the method is being called so many times and fix that, or (b) add a boolean instance variable to the enclosing class, call it something like busySaving. At the start of the method, bail out if busySaving is true, otherwise set it to true an carry on. Change your saveInBackground() to saveInBackgroundWithBlock() and reset the busySaving flag in the completion block.
EDIT
Now we see why it's being called repeatedly: because the notification is being received repeatedly. One way to fix (idea (a) above) would be to stop observing that notification (NSNotificationCenter removeObserver) at the start of getNowPlayingItem. Then, since you want to get subsequent notifications, re-add yourself as an observer after the save, using saveInBackgroundWithBlock. Notice this is different from saveInBackground see here for reference.
Idea (b) above still applies as well, if you prefer.