IOS Monitor audio route changes in background - ios

Is it possible to monitor changes to the Audio route without playing music? I have found this question, but it seems to rely on audio being played.
My current setup looks something like this:
NotificationCenter.default.addObserver(self, selector: #selector(routeChange), name: .AVAudioSessionRouteChange, object: nil)
#objc
func routeChange(n: Notification) {
...
}
The background mode "Audio, AirPlay and Picture in Picture" is enabled.
My setup seems to work fine as long as the app is in foreground. As soon as it is in the background, routeChange will not be called anymore.

Related

Handle IOS app open from tapping on Now Playing

I'm using SwiftAudio to play audio, I want to detect when audio playing in background mode and app become active from tapping on Now Playing.
NotificationCenter
.default
.addObserver(self, selector: #selector(applicationDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil)
#objc func applicationDidBecomeActive() {
print("applicationDidBecomeActive")
}
right now I'm trying this way but it trigger applicationDidBecomeActive even when user open app from icon.
If there any other ways to know when user tapping on Now Playing, please let me know.
I'm very new to IOS development and Swift
Edit: I guess I can detect how app is entered foreground from NowPlaying by passing the expected objectsender to addObserver instead of passing nil object. I tried many times but still don't know what sender object it it.
You should look into MPNowPlayingInfoCenter
When your app is active again, you can check the nowPlayingInfo and playbackState to customise what you actually want to do in your app.
What is the difference between launching from Notification or Now Playing in your case? Maybe clarifying this a bit more will allow us to craft a better solution to your problem.

How to know when MPMusicPlayerController changes playing item naturally

I am using the MPMusicPlayerController to create a music player in my app. I have got it all working great except for one small issue:
When the songs changes naturally - one song finishes and the next starts from the set queue - the notification MPMusicPlayerControllerNowPlayingItemDidChange doesn't appear to be called.
At the moment I am utilising both the MPMusicPlayerControllerNowPlayingItemDidChange and MPMusicPlayerControllerPlaybackStateDidChange notifications. These cover playing, pausing, shuffle, repeat, next, previous etc. When the notifications are hit I then refresh the screen based on the MPMusicPlayerController to show the new song, artist or different button icons required. Neither of these are called though when a song finishes and the next one automatically starts playing - this means that the title and artist of the previous song is left until the user reloads the screen or interacts with the audio controls which is not good user experience.
Short of regularly checking whether the current name matches the playing name I don't know how to update this in the normal flow of the app.
NotificationCenter.default.addObserver(
forName: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange,
object: musicPlayerController,
queue: nil) { _ in
// Update view
}
The answer to this turns out to be very simple but also difficult to spot if you're not looking in the right place.
Before we add our observers we need to begin generating the playback notifications:
musicPlayerController.beginGeneratingPlaybackNotifications()
NotificationCenter.default.addObserver(self,
selector: #selector(refreshView),
name: .MPMusicPlayerControllerPlaybackStateDidChange,
object: musicPlayerController)
NotificationCenter.default.addObserver(self,
selector: #selector(refreshView),
name: .MPMusicPlayerControllerNowPlayingItemDidChange,
object: musicPlayerController)
We also need to remember to end generating them when we leave (deallocate) the view:
deinit {
NotificationCenter.default.removeObserver(self, name: .MPMusicPlayerControllerPlaybackStateDidChange, object: nil)
NotificationCenter.default.removeObserver(self, name: .MPMusicPlayerControllerNowPlayingItemDidChange, object: nil)
musicPlayerController.endGeneratingPlaybackNotifications()
}
The confusion came from the musicMediaPlayer returning a number of notifications even without this which didn't point to the fact we weren't observing all the notifications that were being fired.
Note: It is worth noting that as of the time of writing this it was in discussion whether there was a need to manually remove observers - I have included it here for answer completeness.

Block screen recoding but allow HDMI screen mirroring Swift 4

I need to stop Screen recoding, however I need to allow video sharing through HDMI.
I know the captured notification is usually used for this, but I cant find a way to separate out these two things.
UIScreen.main.addObserver(self, forKeyPath: "captured", options: .new, context: nil)
The above triggers notifications when screen recording is started / stopped. But also triggers when an external display is connected / disconnected (though I'm finding it to be intermittent here).
I have tried using the following to detect if a screen within the UIScreen is mirrored or just to check the screen count. Recordings show only one screen in the count, and mirrored screens should show a count of 2
recordingLabel.text = "count \(UIScreen.screens.count)"
for screen in UIScreen.screens {
if screen.mirrored != nil {
recordingLabel.text = "Mirrored - count \(UIScreen.screens.count)"
}
}
But again this is intermittent. Most of the time the count does not change on connecting a HDMI screen, but does change to 2 on disconnecting.
I have also found using UIScreen notifications work for connected / disconnected. But there seems to be a race condition happening with the captured notification that I still need to handle recordings.
NotificationCenter.default.addObserver(self, selector: #selector(connected), name: UIScreen.didConnectNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(disconnected), name: UIScreen.didDisconnectNotification, object: nil)
Is there any way to detect the type of recording or prevent the race from happening when both types of notifications trigger?
EDIT :
I have since found
NotificationCenter.default.addObserver(self, selector: #selector(captureChanged(notification:)), name: UIScreen.capturedDidChangeNotification, object: nil)
Which works better with the connection notifications, but it still triggers before I get a connected notification. I don't really want to add a timer to detect if the didConnect notification triggers after the captureChange, but that might be the only way

Background function that plays audio only works sometimes on device , all the time on simulator swift 4

I have this in my viewDidLoad :
NotificationCenter.default.addObserver(self, selector: #selector(appEnteredBackgound), name: Notification.Name.UIApplicationDidEnterBackground, object: nil)
To handle when my app is closed. The function appEnteredBackgound plays audio from a non AV library. This code works every-time in simulator when closing and opening the app. Yet on a real device only sometimes. Other times on real device the audio just stops playing, any ideas on why that is?
Your notification for the state where your app is in background but still active....it will be called when app enters background like on click of home button......do not expect it to be called when terminate your app.
Notification.Name.UIApplicationDidEnterBackground.
If you want to play or resume laying your audio even after termination of your app you should got with
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

How do I perform some code after app was connected to the internet and is in the background mode?

In didFinishLaunchingWithOptions I do:
AFNetworkReachabilityManager.shared().startMonitoring()
NotificationCenter.default.addObserver(self, selector: #selector(networkDidChangeStatus), name: NSNotification.Name.AFNetworkingReachabilityDidChange, object: nil)
my function for selector is following:
func networkDidChangeStatus() {
//UPLOAD CHANGES TO ICLOUD DATABASE
}
All I need to do is to upload changes immediately after device is connected to the network even when the app is not opened.
It works pretty well when app is in foreground mode, but do not know what to do when app is in the background mode. Is it possible at all?
I did turn on BACKGROUND MODES in Capabilities tab.

Resources