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)
}
I have read the question about making a-non-voip-call and it seems, that the open url is the only way to do it. Since CoreTelephony is deprecated, is it possible to use Callkit to get the call states when making a call with open url? If not is there any way to get the call states programmatically? I am developing an in-house-app.
How can CallKit be used to make a non-voip call?
Thanks in advance!!
To get call states in CallKit, you can use CXCallObserver in your app.
import CallKit
final class ProviderDelegate: NSObject, CXCallObserverDelegate {
var callObserver: CXCallObserver!
func setupCallObserver(){
callObserver = CXCallObserver()
callObserver.setDelegate(self, queue: nil)
}
func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
if call.hasEnded == true {
print("CXCallState :Disconnected")
}
if call.isOutgoing == true && call.hasConnected == false {
print("CXCallState :Dialing")
}
if call.isOutgoing == false && call.hasConnected == false && call.hasEnded == false {
print("CXCallState :Incoming")
}
if call.hasConnected == true && call.hasEnded == false {
print("CXCallState : Connected")
}
}
}
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?
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.
Developing an app for an iPhone with audio files that need to be listened too through headphones.
How do I check if headphones aren't plugged in so I can tell the user to plug in headphones.
I have the following code from another thread but the audioSessionGetProperty method is deprecated. Anyone know how to alter the following code to make this work OR have there own code/solution.
Thanks.
- (BOOL)isHeadsetPluggedIn {
UInt32 routeSize = sizeof (CFStringRef);
CFStringRef route;
//Maybe changing it to something like the following would work for iOS7?
//AVAudioSession* session = [AVAudioSession sharedInstance];
//OSStatus error = [session setCategory:kAudioSessionProperty_AudioRoute...?
//the line below is whats giving me the warning
OSStatus error = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
&routeSize,
&route);
/* Known values of route:
* "Headset"
* "Headphone"
* "Speaker"
* "SpeakerAndMicrophone"
* "HeadphonesAndMicrophone"
* "HeadsetInOut"
* "ReceiverAndMicrophone"
* "Lineout"
*/
if (!error && (route != NULL)) {
NSString* routeStr = (__bridge NSString*)route;
NSRange headphoneRange = [routeStr rangeOfString : #"Head"];
if (headphoneRange.location != NSNotFound) return YES;
}
return NO;
}
This should work, but I cannot test it right now, I'll do in the evening.
- (BOOL)isHeadsetPluggedIn {
AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
for (AVAudioSessionPortDescription* desc in [route outputs]) {
if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])
return YES;
}
return NO;
}
Just to extend #Antonio's answer. If you need to detect whether the user has pulled out or plugged in the headphone.
#import <AVFoundation/AVFoundation.h>
// [AVAudioSession sharedInstance]; // #Boris edited: you may need it if there is no `AVAudioSession instance` created before. If doesn't work, uncomment this line.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(audioRouteChangeListenerCallback:)
name:AVAudioSessionRouteChangeNotification
object:nil];
// don't forget to `removeObserver:`
// If the user pulls out he headphone jack, stop playing.
- (void)audioRouteChangeListenerCallback:(NSNotification*)notification
{
NSDictionary *interuptionDict = notification.userInfo;
NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
switch (routeChangeReason) {
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
NSLog(#"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
NSLog(#"Headphone/Line plugged in");
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
NSLog(#"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
NSLog(#"Headphone/Line was pulled. Stopping player....");
break;
case AVAudioSessionRouteChangeReasonCategoryChange:
// called at start - also when other audio wants to play
NSLog(#"AVAudioSessionRouteChangeReasonCategoryChange");
break;
}
}
Swift 3:
To check if headphones are connected
extension AVAudioSession {
static var isHeadphonesConnected: Bool {
return sharedInstance().isHeadphonesConnected
}
var isHeadphonesConnected: Bool {
return !currentRoute.outputs.filter { $0.isHeadphones }.isEmpty
}
}
extension AVAudioSessionPortDescription {
var isHeadphones: Bool {
return portType == AVAudioSessionPortHeadphones
}
}
Then you can just print("isHeadphones connected: \(AVAudioSession.isHeadphonesConnected)")
Listening to Changes
In Swift 3 the syntax is this:
func handleRouteChange(_ notification: Notification) {
guard
let userInfo = notification.userInfo,
let reasonRaw = userInfo[AVAudioSessionRouteChangeReasonKey] as? NSNumber,
let reason = AVAudioSessionRouteChangeReason(rawValue: reasonRaw.uintValue)
else { fatalError("Strange... could not get routeChange") }
switch reason {
case .oldDeviceUnavailable:
print("oldDeviceUnavailable")
case .newDeviceAvailable:
print("newDeviceAvailable")
if AVAudioSession.isHeadphonesConnected {
print("Just connected headphones")
}
case .routeConfigurationChange:
print("routeConfigurationChange")
case .categoryChange:
print("categoryChange")
default:
print("not handling reason")
}
}
func listenForNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(handleRouteChange(_:)), name: NSNotification.Name.AVAudioSessionRouteChange, object: nil)
}
Notice use of:
if AVAudioSession.isHeadphonesConnected {
print("Just connected headphones")
}
#Warif's code in Swift 2.0 with little changes ...
func audioRouteChangeListenerCallback (notif: NSNotification){
let userInfo:[NSObject:AnyObject] = notif.userInfo!
println("\(userInfo)")
let routChangeReason = UInt((userInfo[AVAudioSessionRouteChangeReasonKey]?.integerValue)!)
switch routChangeReason {
case AVAudioSessionRouteChangeReason.NewDeviceAvailable.rawValue:
self.println("Headphone/Line plugged in");
break;
case AVAudioSessionRouteChangeReason.OldDeviceUnavailable.rawValue:
//If the headphones was pulled move to speaker
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.Speaker)
} catch _ {
}
self.println("Headphone/Line was pulled. Stopping player....");
break;
case AVAudioSessionRouteChangeReason.CategoryChange.rawValue:
// called at start - also when other audio wants to play
self.println("AVAudioSessionRouteChangeReasonCategoryChange");
break;
default:
break;
}
}
:D
In Swift (as of 1.2):
func headsetPluggedIn() -> Bool {
let route = AVAudioSession.sharedInstance().currentRoute
return (route.outputs as! [AVAudioSessionPortDescription]).filter({ $0.portType == AVAudioSessionPortHeadphones }).count > 0
}
Swift 3.0 version
Method to check if headphones are plugged or any Bluetooth device with audio output connected
func bluetoothOrHeadphonesConnected() -> Bool {
let outputs = AVAudioSession.sharedInstance().currentRoute.outputs
for output in outputs{
if output.portType == AVAudioSessionPortBluetoothA2DP ||
output.portType == AVAudioSessionPortBluetoothHFP ||
output.portType == AVAudioSessionPortBluetoothLE ||
output.portType == AVAudioSessionPortHeadphones {
return true
}
}
return false
}
It's important to check if the headphones are plugged out while you listen any audio.
private func setupObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(self.audioRouteChangeListener), name: .AVAudioSessionRouteChange, object: nil)
}
func audioRouteChangeListener(notification: Notification) {
guard let audioRouteChangeReason = notification.userInfo![AVAudioSessionRouteChangeReasonKey] as? Int else { return }
switch audioRouteChangeReason {
case AVAudioSessionRouteChangeReason.oldDeviceUnavailable.hashValue:
//plugged out
default:
break
}
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(plugout:) name:AVAudioSessionRouteChangeNotification object:nil];
-(void)plugout:(NSNotification*)notification
{
isRemovedHeadset = YES;
}
and handle your code using this isRemovedHeadset boolean in your
if (moviePlayer.playbackState == MPMoviePlaybackStatePaused)
{
if(isRemovedHeadset)
{
isRemovedHeadset = NO;
[moviePlayer prepareToPlay];
[moviePlayer play];
return;
}
}
#Sajjon solution on Swift 5 with RxSwift
func handleRouteChange(_ notification: Notification) {
guard
let userInfo = notification.userInfo,
let reasonRaw = userInfo[AVAudioSessionRouteChangeReasonKey] as? NSNumber,
let reason = AVAudioSession.RouteChangeReason(rawValue: reasonRaw.uintValue)
else { fatalError("Strange... could not get routeChange") }
switch reason {
case .oldDeviceUnavailable:
print("oldDeviceUnavailable")
case .newDeviceAvailable:
print("newDeviceAvailable")
if AVAudioSession.isHeadphonesConnected {
print("Just connected headphones")
}
case .routeConfigurationChange:
print("routeConfigurationChange")
case .categoryChange:
print("categoryChange")
default:
print("not handling reason")
}
}
func listenForNotifications() {
NotificationCenter.default.rx
.notification(AVAudioSession.routeChangeNotification)
.subscribe(onNext: { (n) in
self.handleRouteChange(n)
})
.disposed(by: disposeBag)
}