我在main.storyboard
中使用单视图控制器创建了一个空白示例项目,这是下面的实现:
import UIKit
import AVFoundation
private enum DownloadErrors: Error {
case invalidRequest
case noResponse
case noTemporaryURL
case noCacheDirectory
case inplayable
case fileNotExists
case system(error: Error)
var localizedDescription: String {
switch self {
case .invalidRequest:
return "invalid request"
case .noResponse:
return "no response"
case .noTemporaryURL:
return "no temporary URL"
case .noCacheDirectory:
return "no cache directory"
case .inplayable:
return "invalid to play"
case .fileNotExists:
return "file not exists"
case let .system(error):
return error.localizedDescription
}
}
}
class ViewController: UIViewController {
// MARK: - View Life Cycle
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.test()
}
// MARK: - Private API
private func test() {
self.download(with: self.request, completion: { result in
switch result {
case let .failure(error):
print("failure: \(error.localizedDescription)")
return
case let .success(url):
self.play(atURL: url)
}
})
}
private let request: URLRequest? = {
var components = URLComponents()
components.scheme = "https"
components.host = "islex.arnastofnun.is"
components.path = "/islex-files/audio/10/1323741.mp3"
guard let url = components.url else {
return nil
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("audio/mpeg", forHTTPHeaderField: "Content-Type")
request.setValue(
"attachment; filename=\"1323741.mp3\"",
forHTTPHeaderField: "Content-Disposition"
)
print(request.url?.absoluteString ?? "invalid URL")
return request
}()
private typealias Callback = (Result<URL, DownloadErrors>) -> Void
private func download(with nilableRequest: URLRequest?, completion: @escaping Callback) {
guard let request = nilableRequest else {
completion(.failure(.invalidRequest))
return
}
let task = URLSession.shared.downloadTask(with: request) { (rawTemporaryFileURL, rawResponse, rawError) in
if let error = rawError {
completion(.failure(.system(error: error)))
return
}
guard let httpStatusCode = (rawResponse as? HTTPURLResponse)?.statusCode else {
completion(.failure(.noResponse))
return
}
print("http status code: \(httpStatusCode)")
guard let sourceFileURL = rawTemporaryFileURL else {
completion(.failure(.noTemporaryURL))
return
}
guard let cache = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
completion(.failure(.noCacheDirectory))
return
}
let targetFileURL = cache.appendingPathComponent("audio_10_1323741.mp3")
if !FileManager.default.fileExists(atPath: targetFileURL.path) {
do {
try FileManager.default.moveItem(at: sourceFileURL, to: targetFileURL)
} catch let error {
completion(.failure(.system(error: error)))
}
}
DispatchQueue.main.async {
completion(.success(targetFileURL))
}
}
task.resume()
}
private func play(atURL url: URL) {
do {
guard FileManager.default.fileExists(atPath: url.path) else {
print("play: \(DownloadErrors.fileNotExists)")
return
}
let player = try AVAudioPlayer(contentsOf: url)
player.volume = 1.0
player.prepareToPlay()
player.play()
print("play: finished")
} catch let error {
print("play error: \(error.localizedDescription)")
}
}
}
我做错了什么?我有一个成功的响应,尝试使用url创建音频播放器时没有出现错误,但是我的播放器没有播放任何内容。我尝试从链接下载并立即播放文件:
https://islex.arnastofnun.is/islex-files/audio/10/1323741.mp3
我的Xcode登录控制台:
https://islex.arnastofnun.is/islex-files/audio/10/1323741.mp3
http status code: 200
play: finished
由于您位于完成块内,因此您很可能需要将调用封装在DispatchQueue块内,以便在主线程上显式运行它。简而言之,您可以这样做:
DispatchQueue.main.async {
self.play(atURL: url)
}