2016-11-17 92 views
1

我已经(几乎)成功实现了URLSessionDelegate,URLSessionTaskDelegateURLSessionDataDelegate,以便我可以在后台上传我的对象。但我不知道如何实现完成处理程序,这样我可以删除我发送的对象,当服务器返回statuscode=200实现backgroundSession.uploadTask的完成处理程序

我现在开始uploadTask这样

let configuration = URLSessionConfiguration.background(withIdentifier: "com.example.myObject\(myObject.id)") 
let backgroundSession = URLSession(configuration: configuration, 
            delegate: CustomDelegate.sharedInstance, 
            delegateQueue: nil) 
let url: NSURL = NSURL(string: "https://www.myurl.com")! 
let urlRequest = NSMutableURLRequest(url: url as URL) 

urlRequest.httpMethod = "POST" 
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") 

let uploadTask = backgroundSession.uploadTask(with: urlRequest as URLRequest, fromFile: path) 

uploadTask.resume() 

我尝试添加一个关闭到uploadTask的初始化,但xcode显示一个错误,这是不可能的。

我有我的自定义类CustomDelegate:

class CustomDelegate : NSObject, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDataDelegate { 

static var sharedInstance = CustomDelegate() 

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 
    print("\(session.configuration.identifier!) received data: \(data)") 
    do { 
     let parsedData = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String:Any] 
     let status = parsedData["status"] as! NSDictionary 
     let statusCode = status["httpCode"] as! Int 

     switch statusCode { 
     case 200: 
      // Do something 
     case 400: 
      // Do something 
     case 401: 
      // Do something 
     case 403: 
      // Do something 
     default: 
      // Do something 
     } 
    } 
    catch { 
     print("Error parsing response") 
    } 
} 
} 

它还实现了与会代表的其他功能。

我想知道的是,上传完成后,我可以从CustomDelegate内更新我觉得很难(也许不可能?)的用户界面和数据库。

+0

实现['didCompleteWithError'](https://developer.apple.com/reference/foundation/urlsessiontaskdelegate/1411610-urlsession)。如果你想进度更新,你可以使用['didSendBodyData'](https://developer.apple.com/reference/foundation/urlsessiontaskdelegate/1408299-urlsession)。 – Rob

+0

不相关,但不是实例化NSURL和NSMutableURLRequest并转换为URL和URLRequest,你应该首先创建URL和URLRequest。唯一的窍门是,既然你想改变'URLRequest',使用'var',而不是'let'。 – Rob

+0

@Rob我已经这样做了,但我的问题是如何“摆脱”CustomDelegate类。如果我例如在'mainViewController'中,并且调用一个执行'uploadTask'设置的函数,那么我想更新UI,例如 – Frederik

回答

2

如果你只在检测到请求的完成感兴趣的话,最简单的方法是使用一个封闭:

class CustomDelegate : NSObject, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDataDelegate { 

    static var sharedInstance = CustomDelegate() 
    var uploadDidFinish: ((URLSessionTask, Error?) -> Void)? 

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 
     DispatchQueue.main.async { 
      uploadDidFinish?(task, error) 
     } 
    } 

} 

然后您的视图控制器将发起请求,例如之前设置这个封闭

CustomDelegate.sharedInstance.uploadDidFinish = { [weak self] task, error in 
    // update the UI for the completion here 
} 

// start the request here 

如果您想更新您的多种情况UI(例如,不仅上传完成,但由于上传发送进度),你理论上可以设置多个瓶盖(一个用于完成,一个用于进度),但通常你会采用你自己的委托协议模式。 (就个人而言,我会重新命名CustomDelegate为类似UploadManager避免关于谁是授人以什么困惑,但是这取决于你。)

例如,你可以这样做:

protocol UploadDelegate: class { 
    func didComplete(session: URLSession, task: URLSessionTask, error: Error?) 
    func didSendBodyData(session: URLSession, task: URLSessionTask, bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) 
} 

然后,在你网络请求管理器(您CustomDelegate实现),定义了一个delegate属性:

weak var delegate: UploadDelegate? 

在适当URLSession委托方法,你会打电话给你的自定义委托甲基消耗臭氧层物质沿信息传递给视图控制器:

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 
    // do whatever you want here 

    DispatchQueue.main.async { 
     delegate?.didComplete(session: session, task: task, didCompleteWithError: error) 
    } 
} 

func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { 
    // do whatever you want here 

    DispatchQueue.main.async { 
     delegate?.didSendBodyData(session: session, task: task, bytesSent: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend) 
    } 
} 

然后,你宣布你的视图控制器,以符合新的协议,并实现这些方法:

class ViewController: UIViewController, UploadDelegate { 
    ... 
    func startRequests() { 
     CustomDelegate.sharedInstance.delegate = self 

     // initiate request(s) 
    } 

    func didComplete(session: URLSession, task: URLSessionTask, error: Error?) { 
     // update UI here 
    } 

    func didSendBodyData(session: URLSession, task: URLSessionTask, bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { 
     // update UI here 
    } 
} 

现在,你可能会更新这个UploadDelegate协议捕获模型信息并将其作为参数传递给您的方法,但希望这可以说明基本思想。


一些小意见:

  1. 在创建会话,你应该从你的代码切除NSURLNSMutableURLRequest类型,例如:

    let url = URL(string: "https://www.myurl.com")! 
    var urlRequest = URLRequest(url: url) 
    
    urlRequest.httpMethod = "POST" 
    urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") 
    
    let uploadTask = backgroundSession.uploadTask(with: urlRequest, fromFile: path) 
    
    uploadTask.resume() 
    
  2. 您正在寻找statusCode in didReceiveData。你真的应该这样做,在didReceiveResponse。此外,您通常会从获取状态码。

  3. 您正在解析didReceiveData中的响应。一般来说,您应该在didCompleteWithError中这样做(以防万一需要多次拨打didReceiveData才能收到整个回复)。

  4. 我不知道这myObject.id是什么,但是你所选择的标识符,"com.example.myObject\(myObject.id)",有些犯罪嫌疑人:

    • 你创建的每个对象的新URLSession实例?您可能希望为所有请求提供一个。

    • 当您的应用在上传在后台继续时被暂停/抛弃时,应用重新启动时,您是否有可靠的方式重新实例化相同的会话对象?
       

    一般来说,你会想对所有上传的单个上传会话,并且名称应该是一致的。我不是说你不可以按照你的方式去做,但是它似乎会在没有经过一些额外的工作的情况下重新创建这些会话时出现问题。随你便。

    所有这一切都是说,如果应用程序终止并在上传完成时在后台重新启动,我会确保您测试后台上传过程的工作。这感觉这是不完整/脆弱的,但希望我只是跳到一些不正确的结论,并且你已经完成了所有工作,并且简单地没有分享一些细节(例如你的应用代理的handleEventsForBackgroundURLSession)(为了简洁起见非常感谢)。

+0

谢谢,很好的答案!除了获取状态码之外,我已经完成了它的工作。我认为我错误地实现了'didReceiveResponse',因为它不会被调用。有任何想法,为什么?干杯。 – Frederik

+0

我会仔细检查方法签名并确保是正确的:https://developer.apple.com/reference/foundation/urlsessiondatadelegate/1410027-urlsession。否则,我会建议用[可重现的问题示例]发布一个新问题(http://stackoverflow.com/help/mcve)。 – Rob

+0

@Rob我试图通过后台会话实现下载任务的委托,但我的didFinishDownloadingTo没有被触发。如果你有机会,我会很感激,如果你可以看看:http://stackoverflow.com/questions/41488989/using-urlsession-and-background-fetch-together-with-remote-notifications-using-f ?noredirect = 1#comment70190510_41488989 – user2363025