2017-09-11 21 views
0

ボタンをクリックして、ダウンロードしたファイルを順番にダウンロードしてからダウンロードした後、表示するWebviewを開きます。しかし、すでに開いているwebviewを完了していないファイルをダウンロードしてしまいます。私は数日でいくつかのアプローチを見て、問題を解決する方法を知らない。誰も助けることができますか?Swift 3:ファイルを順次ダウンロードしてViewControllerを開く方法

多くのありがとうございます。

DownloadManagerクラス

class DownloadManager: NSObject { 

    /// Dictionary of operations, keyed by the `taskIdentifier` of the `URLSessionTask` 

    fileprivate var operations = [Int: DownloadOperation]() 

    /// Serial NSOperationQueue for downloads 

    private let queue: OperationQueue = { 
     let _queue = OperationQueue() 
     _queue.name = "download" 
     _queue.maxConcurrentOperationCount = 1 // I'd usually use values like 3 or 4 for performance reasons, but OP asked about downloading one at a time 

     return _queue 
    }() 

    /// Delegate-based NSURLSession for DownloadManager 

    lazy var session: URLSession = { 
     let configuration = URLSessionConfiguration.default 
     return URLSession(configuration: configuration, delegate: self, delegateQueue: nil) 
    }() 

    /// Add download 
    /// 
    /// - parameter URL: The URL of the file to be downloaded 
    /// 
    /// - returns:  The DownloadOperation of the operation that was 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 all 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 for 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) { 
     do { 
      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 
/// 
/// This is abstract to class performs all of the necessary KVN of `isFinished` and 
/// `isExecuting` for a concurrent `Operation` subclass. You can subclass this and 
/// implement asynchronous operations. All you must do is: 
/// 
/// - override `main()` with the tasks that initiate the asynchronous task; 
/// 
/// - call `completeOperation()` function when the asynchronous task is done; 
/// 
/// - optionally, periodically check `self.cancelled` status, performing any clean-up 
/// necessary and then ensuring that `completeOperation()` is called; or 
/// override `cancel` method, calling `super.cancel()` and then cleaning-up 
/// and ensuring `completeOperation()` is 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 { 
     get { 
      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 { 
     get { 
      return stateLock.withCriticalScope { _finished } 
     } 
     set { 
      willChangeValue(forKey: "isFinished") 
      stateLock.withCriticalScope { _finished = newValue } 
      didChangeValue(forKey: "isFinished") 
     } 
    } 

    /// Complete the operation 
    /// 
    /// This will result in the appropriate KVN of isFinished and 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. All Rights Reserved. 
See LICENSE.txt for this sample’s licensing information 

Abstract: 
An extension to `NSLock` to simplify executing critical code. 

From Advanced NSOperations sample code in WWDC 2015 https://developer.apple.com/videos/play/wwdc2015/226/ 
From https://developer.apple.com/sample-code/wwdc/2015/downloads/Advanced-NSOperations.zip 
*/ 

extension NSLock { 

    /// Perform closure within lock. 
    /// 
    /// An extension to `NSLock` to simplify executing critical code. 
    /// 
    /// - parameter block: The closure to be performed. 

    func withCriticalScope<T>(block:() -> T) -> T { 
     lock() 
     let value = block() 
     unlock() 
     return value 
    } 
} 

[ダウンロード]ボタンをクリックしたときに、私はコードの下に呼び出します。

func downloadFiles() { 

    do { 

     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! as 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! as Data) 
     } 


     var totalCount = 0 

     let downloadManager = DownloadManager() 


     for (index, subJson): (String, JSON) in serverJson! { 

      for (_, 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 to 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 to file failed, \(error.localizedDescription)") 
    } 

} 

答えて

0

必要なものは、特定の完了条件でコードが実行されることを約束するものです。現在、リクエストを行い、直ちにwebviewを開きます。要求には時間がかかりますが、コードでは、Webviewを開く前に要求が完了するまで待つようにデバイスに指示していません。この目的でPromiseKitを使用できます。このライブラリを使用すると、複数のリクエストを非同期で送信し、すべてのリクエストが処理されたときにWebviewを開くことができます。どのように動作するかを理解するためには、それを探索する必要があります。基本的には、コード構造は次のようになります。

var promiseArray = [Promise<Void>]() 
for WHATEVER CONDITION YOU WANT TO USE FOR THE LOOP { 
    let promise = YOUR REQUEST GOES HERE 
    promiseArray.append(promise) 
} 
_ = when(fulfilled: promiseArray).then { 
    OPEN WEBVIEW HERE 
} 
関連する問題