我要崩溃了
它说***由于未捕获的异常“NSGenericException”而终止应用程序,原因:“后台会话中不支持完成处理程序块。改用委托。
var Boundary = "\(boundary.generateBoundaryString())_boundary"
private lazy var session: URLSession = {
let config = URLSessionConfiguration.background(withIdentifier: "MyUniqeId")
config.isDiscretionary = true
config.sessionSendsLaunchEvents = true
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
func webServiceForUploadImages(urlStr:String,params:[String:String],fileUrl:String,imageData:Data,success :@escaping (AppMedia) -> Void ,failure:@escaping (NSError) -> Void) -> Void{
let url = Constant.BASE_URL + urlStr
print(url)
if(reachAbility.connection != .none){
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
request.allHTTPHeaderFields = Header.headers()
request.setValue("multipart/form-data; boundary=\(Boundary)", forHTTPHeaderField: "Content-Type")
let data = try! createBody(with: params, filePathKey: "file", paths: [fileUrl], boundary: "\(Boundary)", imageData: imageData)
session.uploadTask(with: request, from: data) { (data, response, err) in
if response != nil{
guard let response = response as? HTTPURLResponse else {return}
handleError.shared.HandleReponseTokenExpireError(dataResponse: response, success: { (response) in
})
if(err != nil){
print("\(err!.localizedDescription)")
}
guard let responseData = data else {
print("no response data")
return
}
if let responseString = String(data: responseData, encoding: .utf8) {
DispatchQueue.main.async {
let dict = Utility.jsonToDict(str: responseString)
let mediaDict = AppMedia(fromDictionary: dict as! [String : Any])
Constant.KAppDelegate.hideProgressHUD()
success(mediaDict)
}
// print("uploaded to: \(responseString)")
}
}else{
DispatchQueue.main.async {
failure(err! as NSError)
Constant.KAppDelegate.hideProgressHUD()
Constant.KAppDelegate.showErrorMessage(title: "Error", message: Constant.ServerError, msgType: .error)
}
}
}.resume()
}else{
self.showErrorMsg(str: Constant.ConnectivityError)
}
}
Run Code Online (Sandbox Code Playgroud)
让 config = URLSessionConfiguration.background(withIdentifier: "MyUniqeId") 使用这个让我崩溃
要使用背景上传URLSessionConfiguration,有一些特殊注意事项:
不能使用完成处理程序(因为当您完成上传时应用程序可能没有运行)。您必须使用基于委托的方法,例如uploadTask(with:fromFile:).
例如:
func startUpload(for request: URLRequest, from data: Data) throws -> URLSessionUploadTask {
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(UUID().uuidString)
try data.write(to: fileURL)
let task = session.uploadTask(with: request, fromFile: fileURL)
task.resume()
return task
}
Run Code Online (Sandbox Code Playgroud)
这显然假设您已经指定delegate并实现了适当的委托方法:
extension BackgroundSession: URLSessionDelegate {
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
self.savedCompletionHandler?()
self.savedCompletionHandler = nil
}
}
}
extension BackgroundSession: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
print(error)
return
}
print("success")
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我们不能使用,uploadTask(with:from:)因为它Data用于第二个参数,这在后台会话中是不允许的。相反,必须将请求的正文保存到文件中,然后使用uploadTask(with:fromFile:).
请记住处理在您的应用程序未运行时上传完成的场景。即,应用程序委托handleEventsForBackgroundURLSession必须捕获完成处理程序。例如,我将有一个属性BackgroundSession来保存完成处理程序:
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
BackgroundSession.shared.savedCompletionHandler = completionHandler
}
Run Code Online (Sandbox Code Playgroud)
然后你想实现urlSessionDidFinishEvents(forBackgroundURLSession:)并调用保存的完成处理程序:
extension BackgroundSession: URLSessionDelegate {
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
self.savedCompletionHandler?()
self.savedCompletionHandler = nil
}
}
}
Run Code Online (Sandbox Code Playgroud)
顺便说一下,在后台下载文件讨论了许多这些注意事项(例如委托 API 而不是基于闭包的 API、应用程序委托问题等)。它甚至讨论了上传任务也是基于文件的要求。
无论如何,这是一个示例BackgroundSession管理器:
import os.log
// Note, below I will use `os_log` to log messages because when testing background URLSession
// you do not want to be attached to a debugger (because doing so changes the lifecycle of an
// app). So, I will use `os_log` rather than `print` debugging statements because I can then
// see these logging statements in my macOS `Console` without using Xcode at all. I'll log these
// messages using this `OSLog` so that I can easily filter the macOS `Console` for just these
// logging statements.
private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: #file)
class BackgroundSession: NSObject {
var savedCompletionHandler: (() -> Void)?
static var shared = BackgroundSession()
private var session: URLSession!
private override init() {
super.init()
let identifier = Bundle.main.bundleIdentifier! + ".backgroundSession"
let configuration = URLSessionConfiguration.background(withIdentifier: identifier)
session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}
}
extension BackgroundSession {
@discardableResult
func startUpload(for request: URLRequest, from data: Data) throws -> URLSessionUploadTask {
os_log("%{public}@: start", log: log, type: .debug, #function)
let fileURL = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(UUID().uuidString)
try data.write(to: fileURL)
let task = session.uploadTask(with: request, fromFile: fileURL)
task.resume()
return task
}
}
extension BackgroundSession: URLSessionDelegate {
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
os_log(#function, log: log, type: .debug)
DispatchQueue.main.async {
self.savedCompletionHandler?()
self.savedCompletionHandler = nil
}
}
}
extension BackgroundSession: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
os_log("%{public}@: %{public}@", log: log, type: .error, #function, error.localizedDescription)
return
}
os_log("%{public}@: SUCCESS", log: log, type: .debug, #function)
}
}
extension BackgroundSession: URLSessionDataDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didBecome downloadTask: URLSessionDownloadTask) {
os_log(#function, log: log, type: .debug)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
os_log("%{public}@: received %d", log: log, type: .debug, #function, data.count)
}
}
Run Code Online (Sandbox Code Playgroud)
而且,当然,我的应用程序委托:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
...
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
BackgroundSession.shared.savedCompletionHandler = completionHandler
}
}
Run Code Online (Sandbox Code Playgroud)
毋庸置疑,上传与 background 一起需要担心很多URLSession。如果您通过慢速连接上传大量资产(例如视频),也许您需要这样做。但另一种(更简单的)替代方法是使用默认URLSession配置,只需告诉操作系统即使用户离开您的应用程序,也请求多一点时间来完成上传。只需将标准完成处理程序模式与 default 结合使用URLSession,并将其与Extending Your App's Background Execution Time 中概述的技术结合使用。现在,这只会为您带来 30 秒左右的时间(过去在较旧的 iOS 版本中为 3 分钟),但通常这就是我们所需要的。但是,如果您认为完成上传可能需要 30 秒以上,那么您将需要 background URLSessionConfiguration。
| 归档时间: |
|
| 查看次数: |
2179 次 |
| 最近记录: |