swift - Implementing completion handlers for backgroundSession.uploadTask -
i have (almost) implemented urlsessiondelegate, urlsessiontaskdelegate, , urlsessiondatadelegate can upload objects in background. i'm not sure how implement completion handlers, can delete object sent, when server returns statuscode=200
i start uploadtask this
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 url) urlrequest.httpmethod = "post" urlrequest.setvalue("multipart/form-data; boundary=\(boundary)", forhttpheaderfield: "content-type") let uploadtask = backgroundsession.uploadtask(with: urlrequest urlrequest, fromfile: path) uploadtask.resume() i tried adding closure initialization of uploadtask xcode displayed error not possible.
i have custom class 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)") { 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: // case 400: // case 401: // case 403: // default: // } } catch { print("error parsing response") } } } it implements other functions delegates.
what want somehow know upload done can update ui , database feel hard (maybe impossible?) within customdelegate.
if you're interested in detecting completion of request, simplest approach use closure:
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) } } } then view controller set closure before initiating request, e.g.
customdelegate.sharedinstance.uploaddidfinish = { [weak self] task, error in // update ui completion here } // start request here if want update ui multiple situations (e.g. not uploads finish, progress uploads sent), theoretically set multiple closures (one completion, 1 progress), you'd adopt own delegate-protocol pattern. (personally, i'd rename customdelegate uploadmanager avoid confusion who's delegate what, that's you.)
for example might do:
protocol uploaddelegate: class { func didcomplete(session: urlsession, task: urlsessiontask, error: error?) func didsendbodydata(session: urlsession, task: urlsessiontask, bytessent: int64, totalbytessent: int64, totalbytesexpectedtosend: int64) } then, in network request manager (your customdelegate implementation), define delegate property:
weak var delegate: uploaddelegate? in appropriate urlsession delegate methods, you'd call custom delegate methods pass along information view controller:
func urlsession(_ session: urlsession, task: urlsessiontask, didcompletewitherror error: error?) { // whatever 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) { // whatever want here dispatchqueue.main.async { delegate?.didsendbodydata(session: session, task: task, bytessent: bytessent, totalbytessent: totalbytessent, totalbytesexpectedtosend: totalbytesexpectedtosend) } } then, you'd declare view controller conform new protocol , implement these methods:
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 } } now, might update uploaddelegate protocol capture model information , pass parameter methods, too, illustrates basic idea.
some minor observations:
when creating session, should excise
nsurl,nsmutableurlrequesttypes code, e.g.: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()you looking
statuscodeindidreceivedata. should doing indidreceiveresponse. also, status codeurlresponse.you parsing response in
didreceivedata. generally, should indidcompletewitherror(just in case takes multiple callsdidreceivedatareceive entire response).i don't know
myobject.idis, identifier you've chosen,"com.example.myobject\(myobject.id)", suspect:are creating new
urlsessioninstance each object? want 1 of requests.when app suspended/jettisoned while upload continues in background, when app restarted, have reliable way of reinstantiating same session objects?
generally you'd want single upload session of uploads, , name should consistent. i'm not saying can't way have, seems it's going problematic recreating sessions without going through work. it's you.
all of i'd make sure test background uploading process works if app terminated , restarted in background when uploads finish. feels incomplete/fragile, i'm jumping incorrect conclusions , you've got working , didn't sharing details (e.g. app delegate's
handleeventsforbackgroundurlsession) sake of brevity (which appreciated).
Comments
Post a Comment