Setting the queue is calling the observer? - ios

I am making a music player, and for some reason when I add the notification center observer and set the queue and play the song it gets called twice. I commented out the play method and it's only called once. I'm not sure how to fix this or whether this is the issue.
didLoad
NotificationCenter.default.addObserver(self, selector: #selector(change), name: .MPMusicPlayerControllerNowPlayingItemDidChange, object: nil)
musicPlayer.beginGeneratingPlaybackNotifications()
change function
#objc func change() {
print(musicPlayer.nowPlayingItem?.title) //called twice
}
function to queue and play
musicPlayer.setQueue(with: queueArr)
musicPlayer.play()

Related

When using AVFoundation, how to listen for Control Center Screen and Notification Center Screen activity

I use AVFoundation for a video recording. When the app goes to the background I stop the capture session and when it enters the foreground I restart the capture session, everything works fine. I also use callKit to listen for incoming phone calls and that works fine too:
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterBackground), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
#objc func appWillEnterBackground() {
// if recording stop recording, stop timer, etc ...
captureSession.stopRunning()
previewLayer = nil
}
#objc func appWillEnterForeground() {
if !captureSession.isRunning {
captureSession.startRunning()
initialize preview layer
}
}
The problem is while the vc with the camera is active (recording or not) and when I swipe from the bottom to bring up the Control Center Screen or swipe from the top to bring down the Notification Center Screen UIApplication.willResignActiveNotification gets called and the capture session is stopped. When I remove either of those screens UIApplication.willEnterForegroundNotification doesn't get called and the capture session is no longer running.
What I want to do is when either of those screens surface I simply use a bool to prevent the capture session from stopping
var haveControlScreensSurfaced = false // toggle this true/false depending when the control screens enter and leave
#objc func appWillEnterBackground() {
if view.window != nil && haveControlScreensSurfaced { return }
// if recording stop recording, stop timer, etc ...
captureSession.stopRunning()
previewLayer = nil
}
How can specifically listen for Control Center Screen and Notification Center Screen activity so that I can toggle my haveControlScreensSurfaced bool value to true/false?
This way works good for an avplayer
Since I needed a capture session long story short I used this. When sliding up/down either the Notification Center Screen or the Control Screen, UIApplication.didEnterBackgroundNotification (app enters background) and UIApplication.willEnterForegroundNotification (app is about to enter foreground) never get called. I simply moved my code to there and problem solved:
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
#objc func didEnterBackground() {
// stop capture session
}
#objc func appWillEnterForeground() {
// start capture session
}
Here is a breakdown of what happens when the notifications are triggered:
Pressing the Home Button, sending app to the background:
1- `UIApplication.willResignActiveNotification` gets called first
2- `UIApplication.didEnterBackgroundNotification` gets called second // *** gets called when the HomeButton is pressed ***
Opening the app back up:
1- `UIApplication.willEnterForegroundNotification` gets called first // *** gets called when the opening the app back up ***
2- `UIApplication.didBecomeActiveNotification` gets called second
Sliding down the Notification Center Screen from the top:
1- `UIApplication.willResignActiveNotification` gets called first
2- `UIApplication.didBecomeActiveNotification` gets called second
3- if using the `.AVCaptureSessionWasInterrupted` the `.videoDeviceNotAvailableInBackground` gets called third
4- `UIApplication.willResignActiveNotification` gets called fourth
Sliding the Notification Center Screen back up:
1- `UIApplication.didBecomeActiveNotification` gets called alone
2- if using the `.AVCaptureSessionInterruptionEnded` it gets called second
Sliding the Control Screen up from the bottom:
1- `UIApplication.willResignActiveNotification` gets called alone
Sliding the Control Screen back back down:
1- `UIApplication.didBecomeActiveNotification` gets called by alone

How to observe adViewWillLeaveApplication Admob

I am putting ads into my app, but when I click on the banner, it seems like there is a delay when calling my observer method to stop all timers, and are only stopped when the app leaves, not when the ad is clicked, which messes things up a little. so I implemented this method into my App delegate:
func adViewWillLeaveApplication(_ bannerView: GADBannerView) {
print("adViewWillLeaveApplication")
}
Now in the scene where I want to detect when an ad is clicked has this code:
NotificationCenter.default.addObserver(self, selector: #selector(pauseTimers), name: /* what goes here? */, object: nil)
I am unsure of what to put within the comments. I cannot find adViewWillLeaveApplication (and I have imported GoogleMobileAds).
What do I need to put instead?

swift AVPlayerItemDidPlayToEndTimeNotification not work

I create a class named DecryptAudioPlayer,it inherit NSObject,this class refrenced a AVPlayer,and observe the notice AVPlayerItemDidPlayToEndTimeNotification when init.like below:
override init() {
super.init()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.playToEnd(_:)), name: AVPlayerItemDidPlayToEndTimeNotification, object: nil)
}
and i have the method:
func playToEnd(notification:NSNotification) {
Log.printLog("notification:\(notification)")
}
but sometimes the player's ower can't recived the AVPlayerItemDidPlayToEndTimeNotification,i am very confused.it seems the AVPlayerItem may don't post AVPlayerItemDidPlayToEndTimeNotification when its end time ,who can tell me why?
I can get the stalled notification to solve the problem. So the question could be put down for a while.

Stop method's execution - Swift

When my watchKit app goes to background it fires the delegate method applicationWillResignActive. Method documentation says it can be used to pause ongoing tasks.
I have an ongoing method that i want to be stopped or broken by the use of the external method. How do i do that?
Example
func method1(){
// performing some actions
}
func breakMethod1(){
// running this method can stop (break) the execution of method1
}
This is, of course, assuming that your app has been architected so that breakMethod1() will definitely cancel the action occurring in method1().
You should set up an observer for an NSNotification at the beginning of method1() like so:
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: "breakMethod1", name: UIApplicationWillResignActiveNotification, object: nil)
And for the sake of cleanup, you should also remove this observer after it's been triggered like so:
notificationCenter.removeObserver(self, name: UIApplicationWillResignActiveNotification, object: nil)

In Swift, how to ivalidate NSTimer in AppDelegate when application going background?

I need to translate my iOS application from obj-c to swift. I have a NStimer in ViewController that loads metadata from shoutcast every 30 seconds, but when application resign active it stops, when enter foreground it runs again.
Edit: OK. Problem solved! I added two observers in viewDidLoad with name UIApplicationWillResignActiveNotification and UIApplicationWillEnterForegroundNotification, like below:
override func viewDidLoad() {
NSLog("System Version is \(UIDevice.currentDevice().systemVersion)");
super.viewDidLoad()
self.runTimer()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "invalidateTimer", name: UIApplicationWillResignActiveNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "runTimer", name: UIApplicationWillEnterForegroundNotification, object: nil)
}
and I made two functions. First one for run timer:
func runTimer(){
loadMetadata()
myTimer.invalidate()
NSLog("timer run");
myTimer = NSTimer.scheduledTimerWithTimeInterval(30.0, target: self, selector: "loadMetadata", userInfo: nil, repeats: true)
let mainLoop = NSRunLoop.mainRunLoop()
mainLoop.addTimer(myTimer, forMode: NSDefaultRunLoopMode)
}
and second to stop it:
func invalidateTimer(){
myTimer.invalidate()
NSLog("timer invalidated %u", myTimer);
}
I hope this can help someone. :)
I suggest you use the appropriate system for your task: https://developer.apple.com/library/ios/documentation/iphone/conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html#//apple_ref/doc/uid/TP40007072-CH4-SW56
Apps that need to check for new content periodically can ask the
system to wake them up so that they can initiate a fetch operation for
that content. To support this mode, enable the Background fetch option
from the Background modes section of the Capabilities tab in your
Xcode project. (You can also enable this support by including the
UIBackgroundModes key with the fetch value in your app’s Info.plist
file.)...
When a good opportunity arises, the system wakes or launches your app
into the background and calls the app delegate’s
application:performFetchWithCompletionHandler: method. Use that method
to check for new content and initiate a download operation if content
is available.

Resources