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.
Related
I am building an app using parse where users can click in annotation in a map and see a picture of that place.
My problem is with the block button ... It does not work, when the user click on the block button, he should not see any annotation by the user he is blocking but that unfortunately does not work .
Here is my block button :
#IBAction func blocking(_ sender: Any) {
let block = PFObject(className: "blocking")
block["me"] = PFUser.current()?.username
block["poster"] = post?.name
block["posterID"] = post?.id
block.saveInBackground { (succ, error) in
if error != nil {
print("rttot")
} else {
self.performSegue(withIdentifier: "mo", sender: nil)
}
}
}
(note the poster ID is the ID of person who post the the picture and poster is the name of the person who made the post)
This action will start a relationship between the the user and the user who is blocking . now I will not show any post that hold the person I am blocking ID .
let queryy = PFQuery(className: "blocking")
queryy.whereKey("me", equalTo: PFUser.current()?.username)
queryy.findObjectsInBackground { (objects, error) in
if error != nil {
print("error")
} else {
for object in objects! {
if let neww = object.object(forKey: "posterID") {
var query = PFQuery(className: "posts")
query.whereKey("id", notEqualTo: neww)
query.findObjectsInBackground { (objects, error) in
if error != nil {
print("error")
} else {
for object in objects! {
let post = Posts()
post.chosen = object.object(forKey: "chosen") as! String
post.lati = object.object(forKey: "lati") as! Double
post.longi = object.object(forKey: "longi") as! Double
post.image = object.object(forKey: "image") as? PFFile
post.text = object.object(forKey: "text") as! String
post.name = object.object(forKey: "name") as! String
post.uid = object.object(forKey: "uid") as! String
post.id = object.object(forKey: "id") as! String
print("999")
var cord = CLLocationCoordinate2D(latitude: post.lati, longitude: post.longi)
let anno = annoClass(cord: cord, post: post, text: "Destination", subText: "ClickMe")
self.myMap.addAnnotation(anno)
}
}
}
}
}
}
}
As you can see it will take the ID of people I am blocking and not showing their posts in the map but that does not work . The app does not show any error or bug btw.
Try This
queryy.whereKey("User_has_Blooked", equalTo: false) // if its equal to true, it should not show the users. make this object in the same class when you fetch users in the map!
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 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.