2016-11-17 13 views
1

URLSessionDelegate, URLSessionTaskDelegateURLSessionDataDelegate을 성공적으로 구현하여 백그라운드에서 내 개체를 업로드 할 수 있습니다. 그러나 나는 내가 보낸 개체를 삭제할 수 있도록 나는 현재 내가 추가 노력이backgroundSession.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을 시작 statuscode=200

서버가 반환 할 때, 완료 핸들러를 구현하는 방법을 잘 모르겠어요 종료로 uploadTask의 초기화가 완료되었지만 xcode가 불가능하다는 오류가 표시되었습니다. 또한 대의원의 다른 기능을 구현

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 있습니다.

내가 원했던 것은 업로드가 완료되어서 내가 느낀 UI와 데이터베이스를 CustomDelegate에서 어렵게 (어쩌면 불가능합니까?) 업데이트 할 수 있다는 것을 어떻게 든 알고 있다는 것입니다.

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

} 

이 그런 다음 뷰 컨트롤러는, 예를 들어, 요청을 시작하기 전에이 폐쇄을 설정합니다 :

+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'를 변형시키고 싶기 때문에'let'이 아닌'var'을 사용하는 것입니다. – Rob

+0

@Rob 나는 그것을했지만 내 문제는 어떻게 "CustomDelegate"클래스에서 빠져 나올 수 있는가이다. 예를 들어'mainViewController'에서 예를 들어'uploadTask'의 설정을하는 함수를 호출하면 UI를 업데이트하고 싶습니다. 예를 들어 – Frederik

답변

2

당신이 요청의 완료를 감지에만 관심이 있다면, 가장 간단한 방법은 클로저를 사용하는 것입니다

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

// start the request here 

여러 상황에 대한 UI (예뿐만 아니라 업로드 완료로하지만, 업로드가 전송로 진행), 당신은 이론적으로 하나를 여러 폐쇄 (완료를 설정할 수 있습니다 업데이트하려면

진행)하지만, 종종 자신 만의 델리게이트 프로토콜 패턴을 채택하게됩니다. (개인적으로, 나는 무엇을에 위임 누구에 대한 혼동을 피하기 위해 UploadManager처럼 뭔가 CustomDelegate의 이름을 변경 싶지만, 그건 당신에게 달려 있습니다.) 예를 들어

을 당신이 할 수 있습니다

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

그런 다음에 당신은 당신의 지정 대리인 메타를 호출 것, 적절한 URLSession 대리자 메서드에서

weak var delegate: UploadDelegate? 

: 네트워크 요청 관리자 (당신의 CustomDelegate 구현)하는 delegate 속성을 정의 당신은 업데이트 할 수 있습니다, 지금

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

:이 방법을 새로운 프로토콜을 준수하고 구현하는 뷰 컨트롤러를 선언하는 것, 그리고

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

: ODS는 뷰 컨트롤러에 정보를 함께 전달하는 이 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. statusCodedidReceiveData입니다. 너 정말 그걸 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