58

To play sound even on Silent mode I use to use below method. But how it's not working.

// Works on Swift 3  
do {
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch {
    print(error)
}

How to get it work in 4.2 / iOS 12?

In newer version we need to set mode and options.

try AVAudioSession.sharedInstance().setCategory(
    <#T##category:AVAudioSession.Category##AVAudioSession.Category#>,
    mode: <#T##AVAudioSession.Mode#>, 
    options: <#T##AVAudioSession.CategoryOptions#>)`
Cœur
  • 37,241
  • 25
  • 195
  • 267
Nitesh
  • 1,564
  • 2
  • 26
  • 53

9 Answers9

74

Her der Töne's comment shows you the new syntax, but you also need to activate the audio session after setCategory:

do {
    try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
    try AVAudioSession.sharedInstance().setActive(true)
} catch {
    print(error)
}
Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159
  • 2
    The above solution forces to drop app compatibility with iOS 9 devices. Is there a way to have iOS 9 compatibility back? – aaberga Aug 23 '18 at 15:20
  • 1
    Are you sure? `setCategory()` has been available since iOS 6 and `setActive()`/`sharedInstance()` since iOS 3. – Rhythmic Fistman Aug 23 '18 at 15:48
  • 2
    @RhythmicFistman yeah, you can check it [here](https://developer.apple.com/documentation/avfoundation/avaudiosession/1771734-setcategory), it's only iOS 10+ – manman Aug 29 '18 at 15:52
  • Ha - does it work if you call the code using Objective-C? Oh, wait, I see the problem - there's a `setCategory:withOptions` (your mode is default anyway) - although I'm not sure what it's called in swift. – Rhythmic Fistman Aug 29 '18 at 22:49
  • Ouch, that's bad. Do it in Objective-C - `AVAudioSession` is a great big lowbrow singleton anyway, configuring it in swift isn't going to improve it much (the opposite in this case). – Rhythmic Fistman Aug 29 '18 at 23:00
  • Thanks Galvin! Had not seen your comment (for long time!!) – aaberga Mar 14 '19 at 10:28
  • gives error String member error: try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default) – SoTm May 20 '19 at 19:53
  • Thanks a lot. Xcode auto-conversion suggestion from Swift 3 to Swift 4.2 couldn't help with this case. – Ahmet Ardal Sep 10 '19 at 18:07
41

As a workaround, you can call the Objective-C AVAudioSession.setCategory: method with the NSObject.performSelector::

if #available(iOS 10.0, *) {
    try! AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
}
else {
    // Workaround until https://forums.swift.org/t/using-methods-marked-unavailable-in-swift-4-2/14949 isn't fixed
    AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:error:"), with: AVAudioSession.Category.playback)
}

If you want to set the category and options for iOS 9 and earlier then use:

AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:withOptions:error:"), with: AVAudioSession.Category.playback, with:  [AVAudioSession.CategoryOptions.duckOthers])

UPDATE:

Issue is fixed in Xcode 10.2.

ricardopereira
  • 11,118
  • 5
  • 63
  • 81
  • Ok, what about 'options' param? How to replace AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.duckOthers]) ? – Fallstreak Sep 19 '18 at 14:01
  • 3
    @Fallstreak try setting the category and options like `AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:withOptions:error:"), with: AVAudioSession.Category.playback, with: [AVAudioSession.CategoryOptions.duckOthers])`. Does it work? – ricardopereira Sep 19 '18 at 14:45
  • Yes, it does. Thanks, mate. – Fallstreak Sep 19 '18 at 15:12
  • perform(NSSelectorFromString("") is risky. I wouldn't do that in a production app – Mallox Sep 19 '18 at 16:12
  • 2
    @Mallox of course you would: there is no safer alternative here, is there? – Cœur Oct 09 '18 at 04:35
  • @Cœur Hope my answer may help. https://stackoverflow.com/a/52753594/2446684 – Galvin Oct 11 '18 at 06:30
  • @Galvin using Objective-C was suggested by Jose M. Gavilán answer already, and doesn't make it safer than an hardcoded string selector. – Cœur Oct 11 '18 at 06:33
  • @Cœur Oh you're right. I didn't read the whole thread inside the link. But I still think it's safer than an hardcoded string. Hardcoded string is unsafe all the time. The compiler can't do any check if anyone change it. – Galvin Oct 11 '18 at 06:37
  • @Galvin if someone can modify your source files without you noticing, you got a bigger issue. – Cœur Oct 11 '18 at 06:42
  • @galvin Even if you work with a team, if someone can modify your source files without you noticing, you got a bigger issue. – highboi Nov 23 '18 at 18:46
  • Fixed in Xcode 10.2: no need of `perform(NSSelectorFromString(` anymore. – Cœur Apr 16 '19 at 02:11
20

For Swift 4.2 in iOS 12, it is simply:

try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [])
Chewie The Chorkie
  • 4,896
  • 9
  • 46
  • 90
15

Update for Xcode 10.2:

Apple finally fix this issue in Xcode 10.2. 
So no need to add these workaround code anymore if you use Xcode 10.2 or newer version.

But you also could refer this code for any problem like this.

You can use an objective-c category to help to fix this issue.

Create a AVAudioSession+Swift.h:

@import AVFoundation;

NS_ASSUME_NONNULL_BEGIN

@interface AVAudioSession (Swift)

- (BOOL)swift_setCategory:(AVAudioSessionCategory)category error:(NSError **)outError NS_SWIFT_NAME(setCategory(_:));
- (BOOL)swift_setCategory:(AVAudioSessionCategory)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError NS_SWIFT_NAME(setCategory(_:options:));

@end

NS_ASSUME_NONNULL_END

With a AVAudioSession+Swift.m:

#import "AVAudioSession+Swift.h"

@implementation AVAudioSession (Swift)

- (BOOL)swift_setCategory:(AVAudioSessionCategory)category error:(NSError **)outError {
    return [self setCategory:category error:outError];
}
- (BOOL)swift_setCategory:(AVAudioSessionCategory)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError {
    return [self setCategory:category withOptions:options error:outError];
}

@end

Then import the "AVAudioSession+Swift.h" in your <#target_name#>-Bridging-Header.h

#import "AVAudioSession+Swift.h"

Result is you can call the method in swift like before.

do {
    try AVAudioSession.sharedInstance().setCategory(.playback)
    try AVAudioSession.sharedInstance().setCategory(.playback, options: [.mixWithOthers])
    try AVAudioSession.sharedInstance().setActive(true)
} catch {
    print(error)
}
Galvin
  • 559
  • 1
  • 6
  • 13
  • This is the best solution. I've used, and it used to work. Now apple decided to fix the issue on Xcode 10.2, so this is not needed anymore. Actually, that makes the call to be ambiguous now, so I had to remove it. Thanks Apple for making my git history uncompilable on the newest Xcode :) – Gabriel Gava Apr 02 '19 at 20:37
  • Fixed in Xcode 10.2: no need for that workaround anymore. – Cœur Apr 16 '19 at 02:12
  • I'm using 10.2.1 and still not able to fetch this method. not even after adding objC files. – Krutika Sonawala Jul 03 '19 at 12:43
7

The above answer (by Rhythmic Fistman) is correct if your app is not compatible with iOS 9 or below.

If your app is compatible with iOS 9 you will see the next error:

'setCategory' is unavailable in Swift

In that case there is a bug with Swift 4.2, this is an issue with AVFoundation in Xcode 10's SDKs, you can work around it by writing an Objective-C function that calls through to the old API, since they're still available in Objective-C.

In the next link you can read more details:

https://forums.swift.org/t/using-methods-marked-unavailable-in-swift-4-2/14949

Cœur
  • 37,241
  • 25
  • 195
  • 267
JMGM
  • 642
  • 6
  • 7
  • An easier workaround than writing Objective-C code is to use [`perform`](https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418764-perform) as described by ricardopereira answer. – Cœur Oct 09 '18 at 11:16
5

Paste this into your viewDidLoad()

do {
    try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: AVAudioSession.Mode.default, options: [])
    try AVAudioSession.sharedInstance().setActive(true)
}
catch {
    print(error)
}
karel
  • 5,489
  • 46
  • 45
  • 50
Will Reynolds
  • 51
  • 1
  • 1
  • you can also add this code in AppDelegate.swift file. below method func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { } – Maulik Patel Apr 12 '20 at 15:33
3
//Swift 4.2
if #available(iOS 10.0, *) {
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, 
    mode: AVAudioSessionModeDefault)
} else {
 //Fallback on earlier versions
}
3

Swift 5

let audioSession = AVAudioSession.sharedInstance()
_ = try? audioSession.setCategory(.playback, options: .defaultToSpeaker)
_ = try? audioSession.setActive(true)
budiDino
  • 13,044
  • 8
  • 95
  • 91
  • does not work! The `.playback` causes error `Error Domain=NSOSStatusErrorDomain Code=-50 "(null)"`. Changing to `.playAndRecord` does help tough. – iKK May 20 '20 at 10:41
1

Code for swift 4 :

do {
        try AKSettings.setSession(category: .playback, with: [])
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, mode: AVAudioSessionModeDefault, options: [])
        try AVAudioSession.sharedInstance().setActive(true)


    } catch {
        print(error)
    }

Hope that helps

Sasan Soroush
  • 857
  • 8
  • 16