iOS 14 在 swift ui 应用程序中下载背景 mp3
Background mp3 download on iOS 14 in a swift ui app
我正在创建一个 swift UI 广播流应用程序,它有一个可以下载的过去剧集的库。我希望用户能够开始下载然后锁定屏幕。目前,这会暂停正在进行的下载。我的下载功能:
func downloadFile(withUrl url: URL, andFilePath filePath: URL) {
URLSession.shared
.downloadTaskPublisher(for: url)
.retry(4)
.map(\.0)
.receive(on: RunLoop.main)
.sink(receiveCompletion: { [self] _ in
downloading = false
downloaded = true
},
receiveValue: { data in
do {
self.downloading = true
try FileManager.default.moveItem(atPath: data.path,
toPath: filePath.path)
} catch {
self.downloaded = false
print("Error: \(error.localizedDescription)")
print("an error happened while downloading or saving the file")
}
})
.store(in: &networkSubscription)
}
其中 .downloadTaskPublisher(for: url)
是:
import Combine
import Foundation
public extension URLSession {
/// Returns a publisher that wraps a URL session download task for a given
/// URL.
///
/// - Parameter url: The URL for which to create a download task.
/// - Returns: A publisher that wraps a download task for the URL.
func downloadTaskPublisher(for url: URL) -> DownloadTaskPublisher {
DownloadTaskPublisher(session: self, request: URLRequest(url: url))
}
/// Returns a publisher that wraps a URL session download task for a given
/// URL request.
///
/// - Parameter request: The URL request for which to create a download task.
/// - Returns: A publisher that wraps a download task for the URL request.
func downloadTaskPublisher(for request: URLRequest) -> DownloadTaskPublisher {
DownloadTaskPublisher(session: self, request: request)
}
}
public struct DownloadTaskPublisher {
fileprivate let session: URLSession
fileprivate let request: URLRequest
}
extension DownloadTaskPublisher: Publisher {
public typealias Output = (URL, URLResponse)
public typealias Failure = Error
public func receive<Subscriber>(subscriber: Subscriber)
where
Subscriber: Combine.Subscriber,
Subscriber.Failure == Failure,
Subscriber.Input == Output
{
let subscription = Subscription(subscriber: subscriber, session: session, request: request)
subscriber.receive(subscription: subscription)
}
}
private extension DownloadTaskPublisher {
final class Subscription {
private let downloadTask: URLSessionDownloadTask
init<Subscriber>(subscriber: Subscriber, session: URLSession, request: URLRequest)
where
Subscriber: Combine.Subscriber,
Subscriber.Input == Output,
Subscriber.Failure == Failure
{
downloadTask = session.downloadTask(with: request, completionHandler: { url, response, error in
guard let url = url, let response = response else {
subscriber.receive(completion: .failure(error!))
return
}
_ = subscriber.receive((url, response))
subscriber.receive(completion: .finished)
})
}
}
}
extension DownloadTaskPublisher.Subscription: Subscription {
fileprivate func request(_: Subscribers.Demand) {
downloadTask.resume()
}
fileprivate func cancel() {
downloadTask.cancel()
}
}
此下载功能会将剧集写入磁盘,但会在应用暂停时被取消。
我想写点像
func downloadFile(withUrl url: URL, andFilePath filePath: URL) {
URLSession.init(configuration: URLSessionConfiguration.background(withIdentifier: "background.download.session"))
.downloadTaskPublisher(for: url)
.retry(4)
.map(\.0)
.receive(on: RunLoop.main)
.sink(receiveCompletion: { [self] _ in
downloading = false
downloaded = true
},
receiveValue: { data in
do {
self.downloading = true
try FileManager.default.moveItem(atPath: data.path,
toPath: filePath.path)
} catch {
self.downloaded = false
print("Error: \(error.localizedDescription)")
print("an error happened while downloading or saving the file")
}
})
.store(in: &networkSubscription)
}
但是,这会引发运行时异常:libc++abi.dylib: terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.' terminating with uncaught exception of type NSException
我想使用 combine idioms 在后台下载文件,避免必须将 AppDelegate
附加到我的 @main struct: App
。
据我了解编译器抛出的错误,我很确定您需要像这样激活后台模式:
我确实找到了 raywenderlich.com 的详细教程,它几乎完全符合您的用例。也许它可以帮助您更深入地挖掘此功能
我正在创建一个 swift UI 广播流应用程序,它有一个可以下载的过去剧集的库。我希望用户能够开始下载然后锁定屏幕。目前,这会暂停正在进行的下载。我的下载功能:
func downloadFile(withUrl url: URL, andFilePath filePath: URL) {
URLSession.shared
.downloadTaskPublisher(for: url)
.retry(4)
.map(\.0)
.receive(on: RunLoop.main)
.sink(receiveCompletion: { [self] _ in
downloading = false
downloaded = true
},
receiveValue: { data in
do {
self.downloading = true
try FileManager.default.moveItem(atPath: data.path,
toPath: filePath.path)
} catch {
self.downloaded = false
print("Error: \(error.localizedDescription)")
print("an error happened while downloading or saving the file")
}
})
.store(in: &networkSubscription)
}
其中 .downloadTaskPublisher(for: url)
是:
import Combine
import Foundation
public extension URLSession {
/// Returns a publisher that wraps a URL session download task for a given
/// URL.
///
/// - Parameter url: The URL for which to create a download task.
/// - Returns: A publisher that wraps a download task for the URL.
func downloadTaskPublisher(for url: URL) -> DownloadTaskPublisher {
DownloadTaskPublisher(session: self, request: URLRequest(url: url))
}
/// Returns a publisher that wraps a URL session download task for a given
/// URL request.
///
/// - Parameter request: The URL request for which to create a download task.
/// - Returns: A publisher that wraps a download task for the URL request.
func downloadTaskPublisher(for request: URLRequest) -> DownloadTaskPublisher {
DownloadTaskPublisher(session: self, request: request)
}
}
public struct DownloadTaskPublisher {
fileprivate let session: URLSession
fileprivate let request: URLRequest
}
extension DownloadTaskPublisher: Publisher {
public typealias Output = (URL, URLResponse)
public typealias Failure = Error
public func receive<Subscriber>(subscriber: Subscriber)
where
Subscriber: Combine.Subscriber,
Subscriber.Failure == Failure,
Subscriber.Input == Output
{
let subscription = Subscription(subscriber: subscriber, session: session, request: request)
subscriber.receive(subscription: subscription)
}
}
private extension DownloadTaskPublisher {
final class Subscription {
private let downloadTask: URLSessionDownloadTask
init<Subscriber>(subscriber: Subscriber, session: URLSession, request: URLRequest)
where
Subscriber: Combine.Subscriber,
Subscriber.Input == Output,
Subscriber.Failure == Failure
{
downloadTask = session.downloadTask(with: request, completionHandler: { url, response, error in
guard let url = url, let response = response else {
subscriber.receive(completion: .failure(error!))
return
}
_ = subscriber.receive((url, response))
subscriber.receive(completion: .finished)
})
}
}
}
extension DownloadTaskPublisher.Subscription: Subscription {
fileprivate func request(_: Subscribers.Demand) {
downloadTask.resume()
}
fileprivate func cancel() {
downloadTask.cancel()
}
}
此下载功能会将剧集写入磁盘,但会在应用暂停时被取消。
我想写点像
func downloadFile(withUrl url: URL, andFilePath filePath: URL) {
URLSession.init(configuration: URLSessionConfiguration.background(withIdentifier: "background.download.session"))
.downloadTaskPublisher(for: url)
.retry(4)
.map(\.0)
.receive(on: RunLoop.main)
.sink(receiveCompletion: { [self] _ in
downloading = false
downloaded = true
},
receiveValue: { data in
do {
self.downloading = true
try FileManager.default.moveItem(atPath: data.path,
toPath: filePath.path)
} catch {
self.downloaded = false
print("Error: \(error.localizedDescription)")
print("an error happened while downloading or saving the file")
}
})
.store(in: &networkSubscription)
}
但是,这会引发运行时异常:libc++abi.dylib: terminating with uncaught exception of type NSException *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.' terminating with uncaught exception of type NSException
我想使用 combine idioms 在后台下载文件,避免必须将 AppDelegate
附加到我的 @main struct: App
。
据我了解编译器抛出的错误,我很确定您需要像这样激活后台模式:
我确实找到了 raywenderlich.com 的详细教程,它几乎完全符合您的用例。也许它可以帮助您更深入地挖掘此功能