gpt4 book ai didi

ios - 如何在后台使用 session.uploadTask 我正在崩溃

转载 作者:行者123 更新时间:2023-12-04 00:24:35 26 4
gpt4 key购买 nike

我要崩溃了

它说 *** 由于未捕获的异常“NSGenericException”而终止应用程序,原因:“后台 session 不支持完成处理程序 block 。改用委托(delegate)人。

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)
}
}

让 config = URLSessionConfiguration.background(withIdentifier: "MyUniqeId")
使用它让我崩溃

最佳答案

使用后台上传URLSessionConfiguration有一些特殊的考虑:

  • 无法使用完成处理程序(因为完成上传后应用程序可能未运行)。您必须使用基于委托(delegate)的方法,例如 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
    }
    这显然假设您已经指定了 delegate并实现了适当的委托(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")
    }
    }
  • 注意,我们不能使用 uploadTask(with:from:)因为那是使用 Data对于第二个参数,后台 session 不允许使用。相反,必须将请求正文保存到文件中,然后使用 uploadTask(with:fromFile:) .
  • 请记住在您的应用程序未运行时处理上传完成的情况。即,应用程序委托(delegate)的 handleEventsForBackgroundURLSession必须捕获完成处理程序。例如,我的 BackgroundSession 中有一个属性。保存完成处理程序:
     func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) { 
    BackgroundSession.shared.savedCompletionHandler = completionHandler
    }
    然后你想实现 urlSessionDidFinishEvents(forBackgroundURLSession:)并调用保存的完成处理程序:
     extension BackgroundSession: URLSessionDelegate {
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
    DispatchQueue.main.async {
    self.savedCompletionHandler?()
    self.savedCompletionHandler = nil
    }
    }
    }

  • 顺便说一句, Downloading Files in the Background讨论了许多这些注意事项(例如,委托(delegate) API 而不是基于闭包的 API、应用程序委托(delegate)问题等)。它甚至讨论了上传任务也是基于文件的要求。

    无论如何,这是一个样本 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)
    }
    }
    当然,我的应用程序委托(delegate):
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {

    ...

    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    BackgroundSession.shared.savedCompletionHandler = completionHandler
    }
    }

    不用说,上传和后台一起使用时要担心很多 URLSession .如果您通过慢速连接上传大量 Assets (例如视频),也许您需要这样做。但另一个(更简单)的替代方法是使用默认的 URLSession配置,并告诉操作系统即使用户离开你的应用程序,请求更多的时间来完成上传。只需使用默认的标准完成处理程序模式 URLSession , 并将其与 Extending Your App's Background Execution Time 中概述的技术结合起来.现在,这只能为您带来 30 秒左右的时间(在旧的 iOS 版本中过去是 3 分钟),但通常这就是我们所需要的。但是如果您认为完成上传可能需要超过 30 秒,那么您需要后台 URLSessionConfiguration .

    关于ios - 如何在后台使用 session.uploadTask 我正在崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58003332/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com