ios - Swift 3 : How to sequential process download files and open ViewController -
i want click button, download number of files sequentially , after complete download, open webview display. encounter download files not completed webview opened. saw approach in days , don't know how fix problem. can help?
many thanks.
downloadmanager class
class downloadmanager: nsobject { /// dictionary of operations, keyed `taskidentifier` of `urlsessiontask` fileprivate var operations = [int: downloadoperation]() /// serial nsoperationqueue downloads private let queue: operationqueue = { let _queue = operationqueue() _queue.name = "download" _queue.maxconcurrentoperationcount = 1 // i'd use values 3 or 4 performance reasons, op asked downloading 1 @ time return _queue }() /// delegate-based nsurlsession downloadmanager lazy var session: urlsession = { let configuration = urlsessionconfiguration.default return urlsession(configuration: configuration, delegate: self, delegatequeue: nil) }() /// add download /// /// - parameter url: url of file downloaded /// /// - returns: downloadoperation of operation queued @discardableresult func adddownload(_ url: url) -> downloadoperation { let operation = downloadoperation(session: session, url: url) operations[operation.task.taskidentifier] = operation queue.addoperation(operation) return operation } /// cancel queued operations func cancelall() { queue.cancelalloperations() } } // mark: urlsessiondownloaddelegate methods extension downloadmanager: urlsessiondownloaddelegate { func urlsession(_ session: urlsession, downloadtask: urlsessiondownloadtask, didfinishdownloadingto location: url) { operations[downloadtask.taskidentifier]?.urlsession(session, downloadtask: downloadtask, didfinishdownloadingto: location) } func urlsession(_ session: urlsession, downloadtask: urlsessiondownloadtask, didwritedata byteswritten: int64, totalbyteswritten: int64, totalbytesexpectedtowrite: int64) { operations[downloadtask.taskidentifier]?.urlsession(session, downloadtask: downloadtask, didwritedata: byteswritten, totalbyteswritten: totalbyteswritten, totalbytesexpectedtowrite: totalbytesexpectedtowrite) } } // mark: urlsessiontaskdelegate methods extension downloadmanager: urlsessiontaskdelegate { func urlsession(_ session: urlsession, task: urlsessiontask, didcompletewitherror error: error?) { let key = task.taskidentifier operations[key]?.urlsession(session, task: task, didcompletewitherror: error) operations.removevalue(forkey: key) } } /// asynchronous operation subclass downloading class downloadoperation : asynchronousoperation { let task: urlsessiontask init(session: urlsession, url: url) { task = session.downloadtask(with: url) super.init() } override func cancel() { task.cancel() super.cancel() } override func main() { task.resume() } } // mark: nsurlsessiondownloaddelegate methods extension downloadoperation: urlsessiondownloaddelegate { func urlsession(_ session: urlsession, downloadtask: urlsessiondownloadtask, didfinishdownloadingto location: url) { { let manager = filemanager.default let destinationurl = try manager.url(for: .documentdirectory, in: .userdomainmask, appropriatefor: nil, create: false) .appendingpathcomponent(downloadtask.originalrequest!.url!.lastpathcomponent) if manager.fileexists(atpath: destinationurl.path) { try manager.removeitem(at: destinationurl) } try manager.moveitem(at: location, to: destinationurl) } catch { print("\(error)") } } func urlsession(_ session: urlsession, downloadtask: urlsessiondownloadtask, didwritedata byteswritten: int64, totalbyteswritten: int64, totalbytesexpectedtowrite: int64) { let progress = double(totalbyteswritten) / double(totalbytesexpectedtowrite) print("\(downloadtask.originalrequest!.url!.absolutestring) \(progress)") } } // mark: nsurlsessiontaskdelegate methods extension downloadoperation: urlsessiontaskdelegate { func urlsession(_ session: urlsession, task: urlsessiontask, didcompletewitherror error: error?) { completeoperation() if error != nil { print("\(error)") } } } /// asynchronous operation base class /// /// abstract class performs of necessary kvn of `isfinished` , /// `isexecuting` concurrent `operation` subclass. can subclass , /// implement asynchronous operations. must is: /// /// - override `main()` tasks initiate asynchronous task; /// /// - call `completeoperation()` function when asynchronous task done; /// /// - optionally, periodically check `self.cancelled` status, performing clean-up /// necessary , ensuring `completeoperation()` called; or /// override `cancel` method, calling `super.cancel()` , cleaning-up /// , ensuring `completeoperation()` called. public class asynchronousoperation : operation { override public var isasynchronous: bool { return true } private let statelock = nslock() private var _executing: bool = false override private(set) public var isexecuting: bool { { return statelock.withcriticalscope { _executing } } set { willchangevalue(forkey: "isexecuting") statelock.withcriticalscope { _executing = newvalue } didchangevalue(forkey: "isexecuting") } } private var _finished: bool = false override private(set) public var isfinished: bool { { return statelock.withcriticalscope { _finished } } set { willchangevalue(forkey: "isfinished") statelock.withcriticalscope { _finished = newvalue } didchangevalue(forkey: "isfinished") } } /// complete operation /// /// result in appropriate kvn of isfinished , isexecuting public func completeoperation() { if isexecuting { isexecuting = false } if !isfinished { isfinished = true } } override public func start() { if iscancelled { isfinished = true return } isexecuting = true main() } } /* copyright (c) 2015 apple inc. rights reserved. see license.txt sample’s licensing information abstract: extension `nslock` simplify executing critical code. advanced nsoperations sample code in wwdc 2015 https://developer.apple.com/videos/play/wwdc2015/226/ https://developer.apple.com/sample-code/wwdc/2015/downloads/advanced-nsoperations.zip */ extension nslock { /// perform closure within lock. /// /// extension `nslock` simplify executing critical code. /// /// - parameter block: closure performed. func withcriticalscope<t>(block: () -> t) -> t { lock() let value = block() unlock() return value } }
then, call below code when click download button.
func downloadfiles() { { var localjson: json? let serverjson = self.serverjson let str = serverjson?.description let data = str?.data(using: .utf8) let filemanager = filemanager.default let documentsurl = filemanager.default.urls(for: .documentdirectory, in: .userdomainmask).first! url let oldmanifesturl = documentsurl.appendingpathcomponent("manifest.json") let oldmanifestpath = oldmanifesturl.path if filemanager.fileexists(atpath: oldmanifestpath) { let jsondata = nsdata(contentsoffile:oldmanifestpath) localjson = json(data: jsondata! data) } var totalcount = 0 let downloadmanager = downloadmanager() (index, subjson): (string, json) in serverjson! { (_, subjson): (string, json) in subjson { let filepath = subjson["path"].stringvalue let nupdated = subjson["updated"].stringvalue if let oupdated = localjson?[index].array?.filter({ $0["path"].string == filepath}).first?["updated"].stringvalue { if (oupdated == nupdated) { continue } } var abspath = filepath let stridx = abspath.index(abspath.startindex, offsetby: 2) if (abspath.hasprefix("./")) { abspath = abspath.substring(from: stridx) } let sourceurl = url(string: self.sourceurl.appending(abspath)) downloadmanager.adddownload(sourceurl!) } } // remove temp json file first if exists. if filemanager.fileexists(atpath: oldmanifestpath) { try? filemanager.removeitem(atpath: oldmanifestpath) } // write temp json file local. try data?.write(to: oldmanifesturl) self.defaults.set(hashes, forkey: "lasthash") let alertcontroller = uialertcontroller(title: "information", message: "download completed", preferredstyle: uialertcontrollerstyle.alert) alertcontroller.addaction(uialertaction(title: "ok", style: uialertactionstyle.default, handler: nil)) self.present(alertcontroller, animated: true, completion: { self.openwebview() }) } catch { print("write json data file failed, \(error.localizeddescription)") } }
what need promise library offers code execution on completion conditions. currently, make request , open webview straight away. requests take time load code doesn't tell device wait requests finish before opening webview. can use promisekit purpose. library, can send multiple requests asynchronously , open webview when of requests have been processed. have explore in order understand how works. basically, code structure this
var promisearray = [promise<void>]() whatever condition want use loop { let promise = request goes here promisearray.append(promise) } _ = when(fulfilled: promisearray).then { open webview here }
Comments
Post a Comment