In my app I have an Audio player that plays audio in background. Everything works as should be however, when I receive a call or I open another app the audio in background in my app goes silent but the remoteControlReceivedWithEvent function is not called.
My function is:
override func remoteControlReceivedWithEvent(event: UIEvent?) {
if event!.type == UIEventType.RemoteControl {
if event!.subtype == UIEventSubtype.RemoteControlPlay {
//print("received remote play")
NSNotificationCenter.defaultCenter().postNotificationName("AudioPlayerIsPlaying", object: nil)
} else if event!.subtype == UIEventSubtype.RemoteControlPause {
//print("received remote pause")
NSNotificationCenter.defaultCenter().postNotificationName("AudioPlayerIsNotPlaying", object: nil)
} else if event!.subtype == UIEventSubtype.RemoteControlNextTrack{
//print("received next")
nextBtn.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}else if event!.subtype == UIEventSubtype.RemoteControlPreviousTrack{
//print("received previus")
backBtn.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}else if event!.subtype == UIEventSubtype.RemoteControlTogglePlayPause{
//print("received toggle")
playBtn.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}
}
}
How can I tell my app that a call is received or that another app has been opened?
Related
I want to do an action after the user tapped on the call button and made a call then returned to the app.
This is my function for making a phone call :
let phoneURL = URL(string: String(format: "tel://%#", phoneNumber.englishNumbers))
UIApplication.shared.open(phoneURL!)
and I have set an observer on CallView in viewDidLoad() like this:
NotificationCenter.default.addObserver(self, selector: #selector (showFeedBack), name: UIApplication.didEnterBackgroundNotification, object: nil)
After I made the call and pressed on the End button (the red button which ends the call). the CallView would appear but the notification won't get called.
Am I using the right notification? or is this the correct approach for detecting when a user made a phone call through your app and came back?
P.S. I have used willResignActiveNotification notification. but it send the notification before even making the call (when the alert is appeared and the user has not select anything yet)
You can use CXCallObserver to listen the call events as below,
import CallKit
class ViewController: UIViewController, CXCallObserverDelegate {
let co = CXCallObserver()
override func viewDidLoad() {
super.viewDidLoad()
co.setDelegate(self, queue: DispatchQueue.main)
}
func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
if call.hasEnded {
print("Call is ended")
}
if call.hasEnded == false && call.hasConnected {
print("Call is connected")
}
if call.isOutgoing {
print("This is an outgoing call")
} else if call.hasEnded == false && call.hasConnected == false {
print("This is an incoming call")
}
}
}
I'm new to Swift development and I'm trying to play/pause on lock screen multiple audio files using:
remoteControlReceived
But it is only allowing me to add it once meaning I can only control one audio file but I'd like 2 other audio files to have play/pause enabled on lock screen.
Below is the code I'm using:
// Playback controls
override func remoteControlReceived(with event: UIEvent?) {
if let event = event {
if event.type == .remoteControl {
switch event.subtype {
case .remoteControlPlay: audioPlayer.play()
case . remoteControlPause: audioPlayer.pause()
default: print("Done")
}
}
}
}
override func remoteControlReceived0(with event: UIEvent?) {
if let event = event {
if event.type == .remoteControl {
switch event.subtype {
case .remoteControlPlay: audioPlayer1.play()
case . remoteControlPause: audioPlayer1.pause()
default: print("Done")
}
}
}
}
func remoteControlReceived(with event: UIEvent?) {
if let event = event {
if event.type == .remoteControl {
switch event.subtype {
case .remoteControlPlay: audioPlayer2.play()
case . remoteControlPause: audioPlayer2.pause()
default: print("Done")
}
}
}
}
but it is only allowing me to add it once meaning I can only control one audio file but I'd like 2 other audio files to have play/pause enabled on lock screen
You can’t. It references only the now playing item.
I have a workout app that plays short clips of sound every couple of seconds. I have background music enabled so that music from other apps can be played while working out. The problem arises when I get a remote push notification (in my case, Slack) that has a sound, which somehow cancels out my duckingOther audio session and the music from other apps becomes loud again.
Question - How do I reset my duckingOthers audiosession when the user gets this type of interruption?
I set the audio session by calling the below function in didFinishLaunchingWithOptions:
private func setupAudioSession(){
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: [.mixWithOthers, .duckOthers, .interruptSpokenAudioAndMixWithOthers])
print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
} catch let error as NSError {
print(error.localizedDescription)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
I have tried treating this as a hard interruption (for example a phone call), but when trying to apply the techniques used for this type of interruption, it seems that remote push notifications pass through the cracks. Below is what I used from a different question to try and catch interruptions.
#objc func handleInterruption(notification: Notification) {
guard let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSessionInterruptionType(rawValue: typeValue) else {
return
}
if type == .began {
// Interruption began, take appropriate actions
print("interruption started")
}
else if type == .ended {
if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
let options = AVAudioSessionInterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// Interruption Ended - playback should resume
print("should resume playback")
setupAudioSession()
} else {
// Interruption Ended - playback should NOT resume
print("should not resume playback")
}
}
}
}
func setupNotifications() {
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self,
selector: #selector(handleInterruption),
name: .AVAudioSessionInterruption,
object: nil)
}
I use the below code to run a Soundcloud based URL audio file.
https://api.soundcloud.com/tracks/<Track Name>/stream?client_id=<my Client id>
var podcastPlayer = STKAudioPlayer.init()
podcastPlayer.delegate = self
podcastPlayer.play(urlString)
It does not go past the buffering state
func audioPlayer(_ audioPlayer: STKAudioPlayer, stateChanged state: STKAudioPlayerState, previousState: STKAudioPlayerState) {
if( state == .playing){
JDStatusBarNotification.dismiss()
updateRemoteNotificationDisplay()
}else if(state == .buffering) {
JDStatusBarNotification.show(withStatus: "Buffering!..Please Wait!", styleName: JDStatusBarStyleRed)
JDStatusBarNotification.showActivityIndicator(true, indicatorStyle: .gray)
}else if(state == .stopped) {
JDStatusBarNotification.dismiss()
}
NotificationCenter.default.post(name: Notification.Name(rawValue: NLConstants.NOTIF_PODCAST_UPDATE_UI), object: nil)
}
So I've got a MPMoviePlayerController playing a video in the background.
If I tell it to load a different video using remote control notifications it works just fine.
However if I tell it to play the video it doesn't play?
Has anyone else had this problem or found a solution?
Code snippet:
override func canBecomeFirstResponder() -> Bool {
return true
}
override func remoteControlReceivedWithEvent(event: UIEvent!) {
if (event.type == UIEventType.RemoteControl){
if (event.subtype.toRaw() == 100 || event.subtype.toRaw() == 101){
didPressPausePlay(self)
}else if(event.subtype.toRaw() == 104){
didPressNext(self)
}else if(event.subtype.toRaw() == 105){
didPressPrevious(self)
}
}
}
#IBAction func didPressPrevious(sender: AnyObject) {
videoTitle.text = ""
if (currentIndex != 0){
currentIndex--
currentVideo = parsedVideoIds[currentIndex] as NSString
videoPlayerViewController = XCDYouTubeVideoPlayerViewController(videoIdentifier: currentVideo);
videoPlayerViewController.moviePlayer.backgroundPlaybackEnabled = true;
videoPlayerViewController.presentInView(self.view);
videoPlayerViewController.moviePlayer.controlStyle = MPMovieControlStyle.None
self.view.bringSubviewToFront(customControls);
videoPlayerViewController.moviePlayer.play()
currentImage = 0
pauseplayButton.setImage(pauseImage, forState: UIControlState.Normal)
}
}
I left out the rest because it all does the same thing.