AFNetworking Reachability Manager in Swift - ios

Is there a way in Swift that the AFNetworking Reachability will continuously checking the internet connection every second, so far this is what I have and it only check one time only:
override func viewDidLoad() {
AFNetworkReachabilityManager.sharedManager().startMonitoring()
AFNetworkReachabilityManager.sharedManager().setReachabilityStatusChangeBlock{(status: AFNetworkReachabilityStatus?) in
switch status!.hashValue {
case AFNetworkReachabilityStatus.NotReachable.hashValue:
print("no internet")
case AFNetworkReachabilityStatus.ReachableViaWiFi.hashValue,AFNetworkReachabilityStatus.ReachableViaWWAN.hashValue:
print("with internet")
default:
print("unknown")
}
}
}
How to check internet connection continuously ?

AFNetworking Reachability does check continuously the connection, I'm using it if a few of my apps and it does the job well. If your code is not working, I believe it might be because you call startMonitoring before setting the handler with setReachabilityStatusChangeBlock, so you might miss an initial event. Also, unrelated to your problem but an improvement you can make, you don't need to use the hashValue in the switch statement, you can use statusdirectly, and you get the benefit of the Swift compiler checking for the completion of the case statements. In summary, give a try to the following version of your code and see if it works:
AFNetworkReachabilityManager.sharedManager().setReachabilityStatusChangeBlock { (status: AFNetworkReachabilityStatus) -> Void in
switch status {
case .NotReachable:
print("Not reachable")
case .ReachableViaWiFi, .ReachableViaWWAN:
print("Reachable")
case .Unknown:
print("Unknown")
}
}
AFNetworkReachabilityManager.sharedManager().startMonitoring()

You should not check for rechability for every minute or periodically. It's not good practice, it's decrease the performance of app.
You can get rechability change notifications. so when rechabilty change you can perform some task
You can do something like this,
You must create a Reachability object before you can receive notifications from it. Also, be sure to call the startNotifier() method on the Reachability object you create. This would be an example of how to do so inside of your application delegate:
class AppDelegate: UIResponder, UIApplicationDelegate
{
private var reachability:Reachability!;
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool
{
NSNotificationCenter.defaultCenter().addObserver(self, selector:"checkForReachability:", name: kReachabilityChangedNotification, object: nil);
self.reachability = Reachability.reachabilityForInternetConnection();
self.reachability.startNotifier();
}
func checkForReachability(notification:NSNotification)
{
// Remove the next two lines of code. You cannot instantiate the object
// you want to receive notifications from inside of the notification
// handler that is meant for the notifications it emits.
//var networkReachability = Reachability.reachabilityForInternetConnection()
//networkReachability.startNotifier()
let networkReachability = notification.object as Reachability;
var remoteHostStatus = networkReachability.currentReachabilityStatus()
if (remoteHostStatus.value == NotReachable.value)
{
println("Not Reachable")
}
else if (remoteHostStatus.value == ReachableViaWiFi.value)
{
println("Reachable via Wifi")
}
else
{
println("Reachable")
}
}
}
You can download reachability class from Apple Documentation
Hope this will help :)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// internetReachable is declared as property.
internetReachable = [Reachability reachabilityForInternetConnection];
[internetReachable startNotifier];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];
}
- (void)checkNetworkStatus:(NSNotification *)notice {
// called when network status is changed
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus)
{
case NotReachable:
{
NSLog(#"The internet is down.");
break;
}
case ReachableViaWiFi:
{
NSLog(#"The internet is Connected.");
break;
}
case ReachableViaWWAN:
{
NSLog(#"The internet is working via WWAN!");
break;
}
}
//#import "Reachability.m"
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target, flags)
NSCAssert(info != NULL, #"info was NULL in ReachabilityCallback");
NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], #"info was wrong class in ReachabilityCallback");
Reachability* noteObject = (__bridge Reachability *)info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
}

You can utilize - https://github.com/ashleymills/Reachability.swift
//declare this inside of viewWillAppear
do {
reachability = try Reachability.reachabilityForInternetConnection()
} catch {
print("Unable to create Reachability")
return
}
NSNotificationCenter.defaultCenter().addObserver(self, selector: "reachabilityChanged:",name: ReachabilityChangedNotification,object: reachability)
do{
try reachability?.startNotifier()
}catch{
print("could not start reachability notifier")
}
//declare this inside of viewWillDisappear
reachability!.stopNotifier()
NSNotificationCenter.defaultCenter().removeObserver(self,
name: ReachabilityChangedNotification,
object: reachability)
func reachabilityChanged(note: NSNotification) {
let reachability = note.object as! Reachability
if reachability.isReachable() {
print("NETWORK REACHABLE.")
}
if reachability.isReachableViaWiFi()
{
print("NETWORK REACHABLE VIA WIFI.")
}
if reachability.isReachableViaWWAN()
{
print("NETWORK REACHABLE VIA WWAN.")
}
else {
print("NETWORK NOT REACHABLE.")
}
}

Also for my own archiving purposes. You can use this one as #Lion in his/her answer suggests.
Just call Reachability.registerListener() inside didFinishLaunchingWithOptions in AppDelegate.swift. It will automatically inform you on changes.
//
// CheckInternet.swift
//
// Created by Dincer on 14/11/15.
//
import AFNetworking
public class Reachability {
private static let theSharedInstance:Reachability = Reachability();
private var isClientOnline:Bool = true;
private var isClientWiFi:Bool = false;
private var isClientConnectionUnknown = false;
func onOnline() {
print("****************************************** Network goes online.");
}
func onOffline() {
print("****************************************** Network goes offline.");
}
func onWiFi() {
print("****************************************** Wifi network on");
}
func onGSM() {
print("****************************************** GSM network on");
}
func onUnknownConnectionStatus() {
print("****************************************** Unkown network status");
}
func isConnectedToNetwork() -> Bool {
return isClientOnline;
}
func isConnectedToWiFi() -> Bool {
return isClientOnline && isClientWiFi;
}
static func sharedInstance() -> Reachability {
return Reachability.theSharedInstance;
}
static func registerListener() {
sharedInstance().registerListener();
}
func registerListener() {
AFNetworkReachabilityManager.sharedManager().setReachabilityStatusChangeBlock { (status: AFNetworkReachabilityStatus) -> Void in
switch status {
case .NotReachable:
self.isClientConnectionUnknown = false;
if self.isClientOnline {
self.isClientOnline = false;
self.onOffline();
}
case .ReachableViaWiFi:
self.isClientConnectionUnknown = false;
if !self.isClientOnline {
self.isClientOnline = true;
self.onOnline();
}
if !self.isClientWiFi {
self.isClientWiFi = true;
self.onWiFi();
}
case .ReachableViaWWAN:
self.isClientConnectionUnknown = false;
if !self.isClientOnline {
self.isClientOnline = true;
self.onOnline();
}
if self.isClientWiFi {
self.isClientWiFi = false;
self.onGSM();
}
case .Unknown:
if !self.isClientConnectionUnknown {
self.isClientConnectionUnknown = true;
self.onUnknownConnectionStatus();
}
}
}
AFNetworkReachabilityManager.sharedManager().startMonitoring();
}
}

Related

unexpected non void return value in void function

I've been pouring over stack overflow for ages trying to find a way out of this error:
unexpected non void return value in void function
that I am getting with returning a Bool within my function.
i just can't seem to dig my way out of this one. I'm sure its something to do with async but I'm not very familiar with these types of functions.
class CheckReachability {
class func setupReachability (hostName:String?, useClosures: Bool) -> Bool{
var reachability : Reachability!
var connected = false
let reachability2 = hostName == nil ? Reachability() : Reachability(hostname: hostName!)
reachability = reachability2
try! reachability?.startNotifier()
if useClosures {
reachability2?.whenReachable = { reachability in
DispatchQueue.main.async {
connected = true
print("Reachable....")
}
}
reachability2?.whenUnreachable = { reachability in
DispatchQueue.main.async {
connected = false
print("Not Connected....")
}
}
} else {
NotificationCenter.default.addObserver(self, selector: Selector(("reachabilityChanged:")), name: ReachabilityChangedNotification, object: reachability2)
}
return connected
}
}
calling this from viewdidload on another vc doesn't allow enough time to get a true result
let connected = CheckReachability.setupReachability(hostName: nil, useClosures: true)
if connected {
Your question is confusing because the code you posted does not have the error you describe. However, you're trying to create a function that returns a result from an async function. That is not how async works.
Async functions start to do a task in the background, where that task won't be finished before it's time to return.
You need to adjust your thinking. Instead of trying to return a result from your function, you need to write your function to take a completion handler. You then call the completion handler once the long-running task has finished (which is after your function has returned.)
#bubuxu provided you with code showing how to modify your function as I described.
If you want to write a checking class to listen to the reachability, define it as a singleton and pass the completeBlock to it like this:
class CheckReachability {
static let shared = CheckReachability()
var reachability: Reachability?
func setupReachability(hostName:String?, completeBlock: ((Bool) -> Void)? = nil) {
reachability = hostName == nil ? Reachability() : Reachability(hostname: hostName!)
try? reachability?.startNotifier()
if let block = completeBlock {
reachability?.whenReachable = { reachability in
DispatchQueue.main.async {
print("Reachable....")
block(true)
}
}
reachability?.whenUnreachable = { reachability in
DispatchQueue.main.async {
print("Not Connected....")
block(false)
}
}
} else {
// If we don't use block, there is no point to observe it.
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(_:)), name: .ReachabilityChangedNotification, object: nil)
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
#objc func reachabilityChanged(_ notification: Notification) {
// ?? what should we do here?
}
}

access reachability with enough time to return true state

i have a reachability class which is checked from within my VC code as below:
func setupReachability (hostName:String?, useClosures: Bool) {
let reachability = hostName == nil ? Reachability() : Reachability(hostname: hostName!)
self.reachability = reachability
try! self.reachability?.startNotifier()
if useClosures {
reachability?.whenReachable = { reachability in
DispatchQueue.main.async {
self.connected = true
print("Reachable....")
}
}
reachability?.whenUnreachable = { reachability in
DispatchQueue.main.async {
self.connected = false
print("Not Connected....")
}
}
} else {
NotificationCenter.default.addObserver(self, selector: Selector(("reachabilityChanged:")), name: ReachabilityChangedNotification, object: reachability)
}
}
this is initialised from within viewDidLoad:
setupReachability(hostName: nil, useClosures: true)
print(self.connected)
the problem i am having is that it always returns false and i need this to load with enough time that it returns true (if it is really connected) at this point. I'm struggling to figure out where to place my setupReachability function and initialise it so that it can be accessed from anywhere in the app and return its connected state. Ive tried placing this in a separate class but can't seem to get it working or access the connected state. Any tips on how to achieve this?

How do I use this code in a singleton in swift?

I have this code that I found here. Now I would like to know how to use it in a singleton. My understanding is if I use this code in a singleton I will be noticed if there is a change in the network status.
func startNetworkReachabilityObserver() {
let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.google.com")
reachabilityManager?.listener = { status in
switch status {
case .NotReachable:
print("The network is not reachable")
case .Unknown :
print("It is unknown whether the network is reachable")
case .Reachable(.EthernetOrWiFi):
print("The network is reachable over the WiFi connection")
case .Reachable(.WWAN):
print("The network is reachable over the WWAN connection")
}
}
// start listening
reachabilityManager?.startListening()
}
Using a singleton is working as I long as you keep a reference of reachabilityManager
class NetworkStatus {
static let sharedInstance = NetworkStatus()
private init() {}
let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.apple.com")
func startNetworkReachabilityObserver() {
reachabilityManager?.listener = { status in
switch status {
case .notReachable:
print("The network is not reachable")
case .unknown :
print("It is unknown whether the network is reachable")
case .reachable(.ethernetOrWiFi):
print("The network is reachable over the WiFi connection")
case .reachable(.wwan):
print("The network is reachable over the WWAN connection")
}
}
reachabilityManager?.startListening()
}
}
So you can use it like this:
let networkStatus = NetworkStatus.sharedInstance
override func awakeFromNib() {
super.awakeFromNib()
networkStatus.startNetworkReachabilityObserver()
}
You will be notified of any change in your network status.
Hoist the variable into the class as a property. Add a static shared property to make your class a singleton.
class ReachabilityManager {
private let networkReachabilityManager = NetworkReachabilityManager()
static let shared = ReachabilityManager()
private override init () {
super.init()
networkReachabilityManager?.listener = { status in
//Post Notifications here
}
}
}
func startListening() {
networkReachabilityManager.startListening()
}
}
The static properties are lazy by default so when you call ReachabilityManager.shared.startListening() it will instantiate the singleton the first time and subsequent calls use the existing shared instance.

How to use NetworkReachabilityManager in Alamofire

I want functionality similar to AFNetworking in Objective-C with Alamofire NetworkReachabilityManager in Swift:
//Reachability detection
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN: {
[self LoadNoInternetView:NO];
break;
}
case AFNetworkReachabilityStatusReachableViaWiFi: {
[self LoadNoInternetView:NO];
break;
}
case AFNetworkReachabilityStatusNotReachable: {
break;
}
default: {
break;
}
}
}];
I am currently using the listener to know the status changes with network
let net = NetworkReachabilityManager()
net?.startListening()
Can someone describe how to support those use cases?
NetworkManager Class
class NetworkManager {
//shared instance
static let shared = NetworkManager()
let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.google.com")
func startNetworkReachabilityObserver() {
reachabilityManager?.listener = { status in
switch status {
case .notReachable:
print("The network is not reachable")
case .unknown :
print("It is unknown whether the network is reachable")
case .reachable(.ethernetOrWiFi):
print("The network is reachable over the WiFi connection")
case .reachable(.wwan):
print("The network is reachable over the WWAN connection")
}
}
// start listening
reachabilityManager?.startListening()
}
}
Start Network Reachability Observer
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// add network reachability observer on app start
NetworkManager.shared.startNetworkReachabilityObserver()
return true
}
}
I found the answer myself i.e by just writing a listener with closure as mentioned below:
let net = NetworkReachabilityManager()
net?.listener = { status in
if net?.isReachable ?? false {
switch status {
case .reachable(.ethernetOrWiFi):
print("The network is reachable over the WiFi connection")
case .reachable(.wwan):
print("The network is reachable over the WWAN connection")
case .notReachable:
print("The network is not reachable")
case .unknown :
print("It is unknown whether the network is reachable")
}
}
net?.startListening()
Here's my implementation. I use it in a singleton. Remember to hold on to the reachability manager reference.
let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.apple.com")
func listenForReachability() {
self.reachabilityManager?.listener = { status in
print("Network Status Changed: \(status)")
switch status {
case .NotReachable:
//Show error state
case .Reachable(_), .Unknown:
//Hide error state
}
}
self.reachabilityManager?.startListening()
}
SWIFT 5
NetworkState Structure
import Foundation
import Alamofire
struct NetworkState {
var isInternetAvailable:Bool
{
return NetworkReachabilityManager()!.isReachable
}
}
Use: -
if (NetworkState().isInternetAvailable) {
// Your code here
}
Using a singleton is working as I long as you keep a reference of reachabilityManager
class NetworkStatus {
static let sharedInstance = NetworkStatus()
private init() {}
let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.apple.com")
func startNetworkReachabilityObserver() {
reachabilityManager?.listener = { status in
switch status {
case .notReachable:
print("The network is not reachable")
case .unknown :
print("It is unknown whether the network is reachable")
case .reachable(.ethernetOrWiFi):
print("The network is reachable over the WiFi connection")
case .reachable(.wwan):
print("The network is reachable over the WWAN connection")
}
}
reachabilityManager?.startListening()
}
So you can use it like this anywhere in your app:
let networkStatus = NetworkStatus.sharedInstance
override func awakeFromNib() {
super.awakeFromNib()
networkStatus.startNetworkReachabilityObserver()
}
You will be notified of any change in your network status.
Just for icing on the cake this is a very good animation to show on your internet connection loss.
Swift 5:
No need for listener object . Just we need to call the closure :
struct Network {
let manager = Alamofire.NetworkReachabilityManager()
func state() {
manager?.startListening { status in
switch status {
case .notReachable :
print("not reachable")
case .reachable(.cellular) :
print("cellular")
case .reachable(.ethernetOrWiFi) :
print("ethernetOrWiFi")
default :
print("unknown")
}
}
}
}
You can start using this function like :
Network().state()
Apple says to use a struct instead of a class when you can. So here's my version of #rmooney and #Ammad 's answers, but using a struct instead of a class. Additionally, instead of using a method or function, I am using a computed property and I got that idea from this Medium post by #Abhimuralidharan. I'm just putting both the idea of using a struct instead of a class (so you don't have to have a singleton) and using a computed property instead of a method call together in one solution.
Here's the struct NetworkState:
import Foundation
import Alamofire
struct NetworkState {
var isConnected: Bool {
// isReachable checks for wwan, ethernet, and wifi, if
// you only want 1 or 2 of these, the change the .isReachable
// at the end to one of the other options.
return NetworkReachabilityManager(host: www.apple.com)!.isReachable
}
}
Here is how you use it in any of your code:
if NetworkState().isConnected {
// do your is Connected stuff here
}
To create NetworkManager Class as follows (For SWIFT 5)
import UIKit
import Alamofire
class NetworkManager {
static let shared = NetworkManager()
let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.apple.com")
func startNetworkReachabilityObserver() {
reachabilityManager?.startListening(onUpdatePerforming: { status in
switch status {
case .notReachable:
print("The network is not reachable")
case .unknown :
print("It is unknown whether the network is reachable")
case .reachable(.ethernetOrWiFi):
print("The network is reachable over the WiFi connection")
case .reachable(.cellular):
print("The network is reachable over the cellular connection")
}
})
}
}
And the usage will be like
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// add network reachability observer on app start
NetworkManager.shared.startNetworkReachabilityObserver()
return true
}
}
Alamofire 5 and above
import Alamofire
// MARK: NetworkReachability
final class NetworkReachability {
static let shared = NetworkReachability()
private let reachability = NetworkReachabilityManager(host: "www.apple.com")!
typealias NetworkReachabilityStatus = NetworkReachabilityManager.NetworkReachabilityStatus
private init() {}
/// Start observing reachability changes
func startListening() {
reachability.startListening { [weak self] status in
switch status {
case .notReachable:
self?.updateReachabilityStatus(.notReachable)
case .reachable(let connection):
self?.updateReachabilityStatus(.reachable(connection))
case .unknown:
break
}
}
}
/// Stop observing reachability changes
func stopListening() {
reachability.stopListening()
}
/// Updated ReachabilityStatus status based on connectivity status
///
/// - Parameter status: `NetworkReachabilityStatus` enum containing reachability status
private func updateReachabilityStatus(_ status: NetworkReachabilityStatus) {
switch status {
case .notReachable:
print("Internet not available")
case .reachable(.ethernetOrWiFi), .reachable(.cellular):
print("Internet available")
case .unknown:
break
}
}
/// returns current reachability status
var isReachable: Bool {
return reachability.isReachable
}
/// returns if connected via cellular
var isConnectedViaCellular: Bool {
return reachability.isReachableOnCellular
}
/// returns if connected via cellular
var isConnectedViaWiFi: Bool {
return reachability.isReachableOnEthernetOrWiFi
}
deinit {
stopListening()
}
}
How to use:
Call NetworkReachability.shared.startListening() from AppDelegate to start listening for reachability changes
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
NetworkReachability.shared.startListening()
// window and rootviewcontroller setup code
return true
}
}
Solution for swift 4* + swift 5* and Alamofire 4.5+
CREATE a NetworkReachabilityManager class from Alamofire and
configure the checkNetwork() method
import Alamofire
class Connectivity {
class func checkNetwork() ->Bool {
return NetworkReachabilityManager()!.isReachable
}
}
USAGE
switch Connectivity.checkNetwork() {
case true:
print("network available")
//perform task
case false:
print("no network")
}
Just slight Improvement in the Alamofire 5
class NetworkManager {
//shared instance
static let shared = NetworkManager()
let reachabilityManager = Alamofire.NetworkReachabilityManager(host: "www.google.com")
func startNetworkReachabilityObserver() {
reachabilityManager?.startListening { status in
switch status {
case .notReachable:
print("The network is not reachable")
case .unknown :
print("It is unknown whether the network is reachable")
case .reachable(.ethernetOrWiFi):
print("The network is reachable over the WiFi connection")
case .reachable(.cellular):
print("The network is reachable over the cellular connection")
}
}
}
}

Are headphones plugged in? iOS7

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)
}

Resources