什么是AVAudioPlayer
AVAudioPlayer是AVFoundation框架里面提供的类,专门用于播放本地或者内存数据中的音频数据。它支持循环播放、多文件同时播放、定点播放等功能。 详情参考这里
基本使用
初始化
self.player = try AVAudioPlayer(data: data)
self.player = try AVAudioPlayer(contentsOf: localFileUrl)
播放/暂停
[player play];
[player pause];
播放完成回调
设置player的代理并实现AVAudioPlayerDelegate协议
self.player?.delegate = self
// MARK: AVAudioPlayerDelegate
extension AudioPlayer: AVAudioPlayerDelegate {
    func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
        progressTimer.invalidate()
    }
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        progressTimer.invalidate()
        playFinished?(flag)
    }
}
播放网络音频文件
由于AVAudioPlayer只可以通过文件或者data实例化,因此可以先下载缓存本地再播放。推荐使用NSURLCache进行文件缓存管理,因为NSURLCache提供了一套API进行缓存保存和读取,方便使用。
/// 播放器管理及数据缓存管理
class AudioPlayerManager: NSObject {
    static let shared = AudioPlayerManager()
    private override init() {
        super.init()
    }
    /// about cache
    private lazy var downloadQueue = DispatchQueue.global()
    private let allowedDiskSize = 100 * 1024 * 1024
    private let diskCachePath = "mp3Cache"
    private lazy var cache = URLCache(memoryCapacity: 0, diskCapacity: allowedDiskSize, diskPath: diskCachePath)
    private lazy var playerHash = [String: AudioPlayer]()
    func prepareDataForPlay(_ path: String, ready: ((AudioPlayer?)->Void)? = nil) {
        guard !IsNilOrEmptyString(path) else {
            ready?(nil)
            return
        }
        downloadQueue.async {
            self.downloadContent(fromUrlString: path, completionHandler: { (result) in
                switch result {
                case .success(let data):
                    // handle data
                    let audioPlayer = AudioPlayer(data: data)
                    DispatchQueue.main.async {
                        self.playerHash[path] = audioPlayer
                        if ready == nil {
                            audioPlayer.play()
                        }
                        ready?(audioPlayer)
                    }
                case .failure(let error):
                    debugPrint(error.localizedDescription)
                    DispatchQueue.main.async {
                        ready?(nil)
                    }
                }
            })
        }
    }
    func stopPlayers() {
        playerHash.values.forEach { (player) in
            player.stop()
        }
    }
}
// MARK: cache
extension AudioPlayerManager {
    typealias DownloadCompletionHandler = (Result<Data, Error>) -> Void
    private func createAndRetrieveURLSession() -> URLSession {
        let sessionConfiguration = URLSessionConfiguration.default
        sessionConfiguration.requestCachePolicy = .returnCacheDataElseLoad
        sessionConfiguration.urlCache = cache
        return URLSession(configuration: sessionConfiguration)
    }
    private func downloadContent(fromUrlString: String, completionHandler: @escaping DownloadCompletionHandler) {
        guard let downloadUrl = URL(string: fromUrlString) else { return }
        let urlRequest = URLRequest(url: downloadUrl)
        // First try to fetching cached data if exist
        if let cachedData = self.cache.cachedResponse(for: urlRequest) {
            print("Cached data in bytes:", cachedData.data)
            completionHandler(.success(cachedData.data))
        } else {
            // No cached data, download content than cache the data
            createAndRetrieveURLSession().dataTask(with: urlRequest) { (data, response, error) in
                if let error = error {
                    completionHandler(.failure(error))
                } else {
                    let cachedData = CachedURLResponse(response: response!, data: data!)
                    self.cache.storeCachedResponse(cachedData, for: urlRequest)
                    completionHandler(.success(data!))
                }
                }.resume()
        }
    }
}