Swift 4.2中AVAudioSession setCategory的可用性

17

迁移到Swift 4.2后,我遇到了多个错误,其中一个很奇怪。它似乎是在Xcode 10中的一个bug,但是否有可用的解决方法?

do {
    try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, with: options)
} catch {
    NSLog("Could not set audio session category")
}

在Swift中,'setCategory(_:with:)'不可用。


OpenRadar: http://www.openradar.me/45397675 - Cœur
该问题仅影响Xcode 10.0和Xcode 10.1。 它已在Xcode 10.2中修复。 - Cœur
4个回答

37

iOS 10+

如果你的目标是,那么只需过渡到新的API并使用:

try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [])

旧版iOS

当您尝试针对一个旧版本的iOS(例如iOS 9)的应用程序进行此操作时,将会出现setCategory(_:mode:options:)'仅在iOS 10.0或更高版本上可用错误。

这已被报告为Apple API中的错误并在Xcode 10.2中得到修复。对于旧版本的Xcode(例如Xcode 10.1),我发现有一种解决方法。当您创建一个Objective-C帮助程序时,因为该API仍然是Objective-C的公开接口,因此仍然可以访问旧的API。

解决方法1:.perform()方法

如果您想要一个快速的内联修复而无需错误处理,可以使用.perform()方法调用Obj.-C API:

if #available(iOS 10.0, *) {
  try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
} else { 
  // Set category with options (iOS 9+) setCategory(_:options:)       
  AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:withOptions:error:"), with: AVAudioSession.Category.playback, with:  [])

  // Set category without options (<= iOS 9) setCategory(_:)
  AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:error:"), with: AVAudioSession.Category.playback)
}

解决方法2:帮助类方法

如果你想对错误有更多的控制,下面是如何立即实现的步骤:

  1. 创建一个新的Objective-C文件,在我的情况下是AudioSessionHelper.m。如果提示是否应该创建桥接头文件,请单击(如果您的项目中没有这个文件)。
  2. 创建一个新的Header文件AudioSessionHelper.h
  3. 插入代码
#ifndef AudioSessionHelper_h
#define AudioSessionHelper_h
#import <AVFoundation/AVFoundation.h>

@interface AudioSessionHelper: NSObject
+ (BOOL) setAudioSessionWithError:(NSError **) error;
@end

#endif /* AudioSessionHelper_h */

#import "AudioSessionHelper.h"
#import <Foundation/Foundation.h>

@implementation AudioSessionHelper: NSObject

+ (BOOL) setAudioSessionWithError:(NSError **) error {
    BOOL success = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:error];
    if (!success && error) {
        return false;
    } else {
        return true;
    }
}
@end
  1. 将你的辅助类插入到桥接头文件中
#import "AudioSessionHelper.h"
  1. 在你的Swift项目中使用它
if #available(iOS 10.0, *) {
    try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
} else {
    try AudioSessionHelper.setAudioSession()
}

这种方法不美观,会给你的项目添加大量不必要的代码和文件,因此如果你现在急需或必须使用Swift 4.2在Xcode 10.1上,请使用它。在所有其他情况下,最好使用Xcode 10.2。


幸运的是,我只针对iOS 10及以上版本进行开发,因此我可以切换到.default模式。问题是,.default模式是否与旧API的精确等效,而旧API没有mode参数? - Deepak Sharma
苹果公司将其描述为默认音频会话模式,并指出您可以将此模式与每个音频会话类别一起使用,因此如果没有其他模式更适合您的用例,那么使用它应该是安全的。 - Benno
你当前使用 perform 的代码没有很好地处理错误。你的 Objective-C 实现很好,但我决定直接在 Swift 4.1 中进行这个解决方法。我为此制作了一个 pod:pod 'AVAudioSessionSetCategorySwift'。请查看我的回答。 - Cœur
@ScottyBlades 尝试使用 AVAudioSession.Category.playback https://developer.apple.com/documentation/avfoundation/avaudiosession/category/1616509-playback - Benno
谢谢@Benno,不幸的是,那并没有起作用。转换为Swift 5后,这个问题就不存在了。 - ScottyBlades

1
这是Xcode 10 SDK中AVFoundation存在的问题。您可以通过编写一个Objective-C函数来绕过它,该函数调用旧的API,因为它们在Objective-C中仍然可用。但是,如果您只针对iOS 10或更高版本进行开发,您可以使用Swift编写。
do{
    if #available(iOS 10.0, *) {
        try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: options)
    } else {
        //the following line is now "unavailable", hence there is no way to support iOS <10
        //try AVAudioSession.sharedInstance().setCategory(.playAndRecord, with: options)
    }
} catch let error {
    print("Could not set audio session category: \(error.localizedDescription)")
}

来源:Swift论坛


1
我所做的是调用setCategory(_:mode:options:)。可以省略options:参数,如果没有模式,可以使用.default模式。

1
“setCategory(:mode:options:)”仅适用于iOS 10.0或更高版本。我认为仍在使用“setCategory(:with:)”并寻求此Stack Overflow问题的帮助的人大多是针对旧版iOS的用户,因此Benno答案获得了成功。 - Cœur

0

该问题已在Xcode 10.2中得到解决。


对于 Xcode 10.1 和 iOS 9(以及更早版本),唯一的其他解决方案来自 Benno,即依赖于 Objective-C 或 perform(NSSelectorFromString("setCategory:error:"))。不幸的是,给定的代码不能很好地处理错误,因此我想介绍一个简单的解决方案,即在使用 Xcode 10.1 时仅回退到 Swift 4.1 在一个单独的框架中(即使您的项目正在使用 Swift 4.2,它也可以工作)。

诀窍就是简单地构建一个单独的框架(或 lib),用较旧版本的 Swift(如4.1)包装对setCategory(_:) 的调用。在这个框架中,您只需要有:

import AVFoundation

extension AVAudioSession {
    @available (iOS 3.0, tvOS 9.0, watchOS 2.0, *)
    @objc open func setCategorySwift(_ category: String) throws {
        try setCategory(category)
    }
}

(也可通过{{link1:pod 'AVAudioSessionSetCategorySwift'}}获得)

然后,回到您的Swift 4.2项目(最终进行pod install),您可以像这样简单地使用它:setCategorySwift(AVAudioSession.Category.playback.rawValue)。一个更长的例子,带有try-catch,可能是:

import AVAudioSessionSetCategorySwift

let session = AVAudioSession.sharedInstance()
do {
    if #available(iOS 10.0, *) {
        try session.setCategory(AVAudioSession.Category.playback, mode: .default, options: [])
    } else {
        // Swift 4.2 workaround: https://github.com/coeur/AVAudioSessionSetCategorySwift
        try session.setCategorySwift(AVAudioSession.Category.playback.rawValue)
    }
    try session.setActive(true)
} catch {
    print("AVAudioSession.setCategory error: \(error)")
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接