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
,nsmutableurlrequest
types 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
statuscode
indidreceivedata
. should doing indidreceiveresponse
. also, status codeurlresponse
.you parsing response in
didreceivedata
. generally, should indidcompletewitherror
(just in case takes multiple callsdidreceivedata
receive entire response).i don't know
myobject.id
is, identifier you've chosen,"com.example.myobject\(myobject.id)"
, suspect:are creating new
urlsession
instance 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