2017-09-11 2 views
0

버튼을 클릭 한 다음 순차적으로 파일을 다운로드하고 다운로드를 완료 한 후 웹 뷰를 열어보고 싶습니다. 그러나 이미 열어 놓은 webview를 완료하지 않은 파일을 다운로드합니다. 며칠 만에 어떤 접근법을 보았고 문제를 해결하는 방법을 모릅니다. 누구든지 도와 줄 수 있습니까?스위프트 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

당신이 필요한 것은 특정 완료 조건에서 코드 실행을 제공하는 약속 라이브러리입니다. 현재 요청을 작성하고 웹보기를 바로 엽니 다. 요청을로드하는 데 시간이 걸리지 만 코드가 웹 뷰를 열기 전에 요청이 완료 될 때까지 대기하도록 장치에 지시하지 않습니다. 이 목적으로 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 
} 
관련 문제