I'm working on an app for which I would like to:
respect the ring/silent switch when playing audio, and
display an icon indicating that sound is muted when the ring/silent switch is set to silent.
Requirement 1 is easy: I'm using AVAudioSessionSoloAmbient
as my app's audio session category, so that my audio session will mute itself when the ring/silent switch is off.
Requirement 2 seems considerably harder, because I need some sort of callback, notification, or KVO that will allow me to monitor the position of the switch, but Apple has made it clear that it is unwilling to offer an officially exposed way of doing this. That said, if I can find a nonintrusive way to monitor the switch's position, even one that is technically prohibited (like, say, an internal NSNotification
), I would be willing to run it by Apple.
Further, I would prefer not to implement some of the polling solutions I've found elsewhere. See the Related Questions section for an example.
What I've Learned (aka What Doesn't Work)
In iOS versions 4 and 5, at least, there was a trick that could be used to get the switch's position by watching the route property of the current audio session. Besides being deprecated by the
AVAudioSession
class, I can confirm that this trick is no longer an option. The current route, both as reported by the C functions comprising the deprecatedAudio Session
API and the currentAVAudioSession
class does not change when the ring/silent switch is toggled.AVSystemController
is an internal class that seems to have a lot of promise. Invoking- (BOOL)toggleActiveCategoryMuted
on thesharedAVSystemController
does indeed mute my app's audio. Further, the shared singleton posts anAVSystemController_SystemVolumeDidChangeNotification
notification when the system volume is changed via the volume buttons. Unfortunately, this notification is not posted in response to changes to the ring/silent switch (though this dubiously attributed source says it should).As far as I can tell, there are no
NSNotification
s posted by any object in response to ring/silent switch position changes. I came to this conclusion after adding myself as an observer to all notifications in the default center:[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:nil object:nil];
and then toggling the ring/silent switch. Nothing.
The
AVSystemController
class has a promising method with the signature:- (BOOL)getActiveCategoryMuted:(BOOL*)arg1;
However, there are two problems with this:
- Neither the return value nor the
BOOL
pointed to byarg1
ever seem to change in response to toggling the ring/silent switch. - Because of the method signature, this method is not (so far as I understand) a candidate for KVO.
- Neither the return value nor the
I suspect that some object sends some other object(s) a
GSEventRef
when the mute switch is changed, because I see the following in the declaration for event types:kGSEventRingerOff = 1012, kGSEventRingerOn = 1013,
However, I'm pretty sure I can't intercept those messages, and even if I could, that would be a bit more than "a little" intrusive.
Why I Believe This Is Possible
Put simply: the Instagram app exhibits essentially this behavior. When you watch a video, it respects the ring/silent switch's setting, but displays an icon when the switch is off. The icon disappears and reappears so immediately after moving the switch that I assume it must be event-based, not polling.
Related Questions
This question dates back to iOS 4, and uses the methods that I mentioned in my first bullet above.
This question is (much) more current, asking about iOS 7. However, because I'm willing to accept a minimally intrusive breaking of the private-API rules, I would contend that this is a different question from my own.
This answer suggests using a polling method that I would strongly prefer to avoid.