2016-11-17 91 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