ios - AFNetworking 3.0 在应用移至后台时继续上传

标签 ios swift afnetworking afnetworking-3

我的应用在前台开始向服务器上传内容。由于此过程可能需要一些时间,因此用户可以很好地将应用程序转移到后台。

我正在使用 AFHTTPSessionManager 提交上传:

let sessionManager = AFHTTPSessionManager()
sessionManager.requestSerializer.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-type")
sessionManager.POST(urlStr, parameters: params, constructingBodyWithBlock: { (formData) -> Void in
    formData.appendPartWithFileData(dataObject, name: "object", fileName: mimeType == "image/jpg" ? "pic.jpg" : "pic.mp4", mimeType: mimeType)
}, progress: { (progress) -> Void in
}, success: { (task, responseObject) -> Void in
    print(responseObject)
    success(responseObject: responseObject)
}, failure: { (task, error) -> Void in
    print(error)
    if let errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] as? NSData {
        do {
            let json = try NSJSONSerialization.JSONObjectWithData(errorData, options: NSJSONReadingOptions.MutableContainers)
            failure(error: error, responseObject: json)
        } catch let error {
            print(error)
        }
    }
})

我需要这个过程在应用程序处于后台状态时继续,直到完成。有一个很好的 SO 答案 here这让我走上了正确的轨道,但在某些地方我仍然处于黑暗之中。我尝试使用链接答案中的“BackgroundSessionManager”类将我的上传调用更改为此:

BackgroundSessionManager.sharedManager().POST(NetworkManager.kBaseURLString + "fulfill_request", parameters: params, constructingBodyWithBlock: { (formData) -> Void in
    formData.appendPartWithFileData(dataObject, name: "object", fileName: mimeType == "image/jpg" ? "pic.jpg" : "pic.mp4", mimeType: mimeType)
}, progress: nil, success: nil, failure: nil)?.resume()

并将其添加到我的 AppDelegate 中:

func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: () -> Void) {
    BackgroundSessionManager.sharedManager().savedCompletionHandler = completionHandler
}

但是我在一些后台线程上遇到了 EXC_BAD_ACCESS 崩溃,几乎没有信息。我是否正确地处理了这个问题?

最佳答案

一些观察:

  1. 您正在构建一个多部分请求,但随后将 Content-Type header 强制为 application/x-www-form-urlencoded。但这不是多部分请求的有效内容 header 。我建议删除请求 header 的手动配置(或告诉我们您这样做的原因)。 AFNetworking 将为您设置Content-Type

  2. 您根本不想理会后台 session ,您可能只想请求一点时间来完成请求,即使在用户离开应用后也是如此。

    var backgroundTask = UIBackgroundTaskInvalid
    
    func uploadImageWithData(dataObject: NSData, mimeType: String, urlStr: String, params: [String: String]?, success: (responseObject: AnyObject?) -> (), failure: (error: NSError, responseObject: AnyObject) -> ()) {
        let app = UIApplication.sharedApplication()
    
        let endBackgroundTask = {
            if self.backgroundTask != UIBackgroundTaskInvalid {
                app.endBackgroundTask(self.backgroundTask)
                self.backgroundTask = UIBackgroundTaskInvalid
            }
        }
    
        backgroundTask = app.beginBackgroundTaskWithName("com.domain.app.imageupload") {
            // if you need to do any addition cleanup because request didn't finish in time, do that here
    
            // then end the background task (so iOS doesn't summarily terminate your app
            endBackgroundTask()
        }
    
        let sessionManager = AFHTTPSessionManager()
        sessionManager.POST(urlStr, parameters: params, constructingBodyWithBlock: { (formData) -> Void in
            formData.appendPartWithFileData(dataObject, name: "object", fileName: mimeType == "image/jpg" ? "pic.jpg" : "pic.mp4", mimeType: mimeType)
            }, progress: { (progress) -> Void in
            }, success: { (task, responseObject) -> Void in
                print(responseObject)
                success(responseObject: responseObject)
                endBackgroundTask()
            }, failure: { (task, error) -> Void in
                print(error)
                if let errorData = error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] as? NSData {
                    do {
                        let json = try NSJSONSerialization.JSONObjectWithData(errorData, options: NSJSONReadingOptions.MutableContainers)
                        failure(error: error, responseObject: json)
                    } catch let error {
                        print(error)
                    }
                }
                endBackgroundTask()
        })
    }
    
  3. 我建议添加 exception breakpoint看看这是否能帮助您找到违规行。

    坦率地说,如果异常在 NSURLSession 内部处理中异步发生,这可能无济于事,但这是我在尝试追踪异常源时首先尝试的事情。

  4. 如果您真的觉得必须使用后台 session (即您的上传任务可能需要超过 beginBackgroundTaskWithName 允许的三分钟),那么请注意后台任务必须是上传或下载任务。

    但是,我相信 POST 方法会创建一个数据任务。在 iOS 7 中,您根本不允许在后台 session 中使用数据任务。在 iOS 8 中,您可以使用后台任务启动数据任务,但您必须使用 NSURLSessionResponseBecomeDownload 响应 didReceiveResponse,例如在 BackgroundSessionManagerconfigureDownloadFinished 中,您可以尝试添加:

    [self setDataTaskDidReceiveResponseBlock:^NSURLSessionResponseDisposition(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response) {
        return NSURLSessionResponseBecomeDownload;
    }];
    
  5. 或者,如果您再次必须使用后台 session ,那么您可能需要手动构建您的 NSURLRequest 对象并将其作为上传或下载任务提交,而不是使用 发布。 AFNetworking 提供了将请求的构建和请求的提交分离的机制,因此如果您需要走那条路,请告诉我们,我们可以向您展示如何做到这一点。

最重要的是,与使用后台 NSURLSession 相比,beginBackgroundTaskWithName 将更容易请求一点时间来完成请求。仅在绝对必要时才执行后者(例如,完成请求很可能需要三分钟以上)。

关于ios - AFNetworking 3.0 在应用移至后台时继续上传,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35321817/

相关文章:

ios - 检查缓存的 HTTP 响应的有效性

ios - 如何使用导航 Controller 对 Segue 进行单元测试

ios - 更少的 Storyboard序列和更多的代码是否会使应用程序更快/更小?

ios - Epub 字体修改不起作用

ios - 使用 AFNetworking/AFHTTPRequestOperation 处理意外的响应内容类型

iphone - AFNetworking 自动将请求包装在 JSON 中?

ios - 知道 UICollectionView 是否在滚动

swift - Google map iOS SDK 动画 map 填充 SwiftUI

swift - 以编程方式退出终端 Mac 应用程序?

iphone - AFNetworking:返回 200 且无内容时运行失败 block