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.
Related
new programmer trying to learn Swift and I'm trying to set my app up to only show the current users post on a parse photosharing database. The first method here in theory should add the current user to the "followingWho" array.
override func viewDidLoad() {
super.viewDidLoad()
let userQuery = PFUser.query()
userQuery?.whereKey("username", equalTo: PFUser.currentUser()!.username!)
userQuery?.findObjectsInBackgroundWithBlock ({
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
//no error
if let objects = objects {
for object in objects {
let followingWho = object["followingWho"] as! NSArray!
self.loadData(followingWho)
}
}
} else {
//error
NSLog("Error")
}
})
self.tableView.reloadData()
}
then my second method here should display the posts and filter out all but the current users
func loadData(followingWho: NSArray) {
let query = PFQuery(className: "Posts")
query.whereKey("addedBy", containedIn: followingWho as! [PFObject])
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock {
(posts: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
//No error
//let posts = posts as! [PFObject]
if let posts = posts {
for post in posts {
self.images.append(post["Image"] as! PFFile)
self.imageCaptions.append(post["Caption"] as! String)
self.imageDates.append(post["date"] as! String)
self.imageUsers.append(post["addedby"] as! String)
}
self.tableView.reloadData()
}
} else {
//error
NSLog("Error")
}
}
}
But nothing seems to be working. The code compiles and runs but it seems to only pull up a blank screen loading none of the posts.
This could be the problem, although if it is not the problem, it is certainly a problem: you are trying to update the UI from a background thread. findObjectsInBackgroundWithBlock calls its completion block in the background, and updating UI from the background is a big no-no. Instead, call your UI updates on the main thread:
dispatch_async(dispatch_get_main_queue(), {
//Do your UI updates here
})
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 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
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.
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.