TO Download thumbnail data for files
//
// SPThumbnailGenerator.swift
// SROperations
//
// Created by Subhr Roy on 21/08/17.
// Copyright © 2017 Subhr Roy. All rights reserved.
//
import Foundation
import CoreData
class SPThumbnailGenerator: SROperation,URLSessionTaskDelegate {
var storeFilePath : String?
var saveFilePath : String?
var url : URL?
var fileName : String?
fileprivate (set) var downloadTask : URLSessionDownloadTask?
fileprivate (set) var dataTask : URLSessionDataTask?
var category : OperationCategory?
fileprivate (set) var progressContentLength : Int64 = 0
fileprivate (set) var expectedContentLength : Int64 = 0
fileprivate (set) var thumbnailDownloadURL : URL?
fileprivate (set) var fileThumbnailResponseData : Data?
//MARK:--------Init Thumbnail Downloader---------//
func initiate(_ urlString : String? = nil , _hasIdentifier : String? = nil, _groupIdentifier : String? = nil, _storefilePath : String? = nil, _saveFilePath : String? = nil, operationQueue : OperationQueue? = OperationQueue.main ,inProgress : Bool = false , isPreSign : Bool = false , opDelegate: OperationTaskDelegate? = nil , operationCategory : OperationCategory = OperationCategory.thumbnailDownload ,priority : OperationPriority? = OperationPriority.high , isCopy : Bool = false) throws -> Bool{
self.storeFilePath = _storefilePath
self.saveFilePath = _saveFilePath
self.isVisualEffectEnable = false
self.category = operationCategory
super.initiate(_hasIdentifier, groupIdentifier: _groupIdentifier , progress: inProgress , delegate:opDelegate ,priority:priority,isCopy: isCopy)
if let fileURL = urlString {
self.url = URL(string: fileURL)
}else{
throw Result.Failure(ErrorType.notFound)
}
if isNotNull(_hasIdentifier){
//Fetch
return true
}else{
throw Result.Failure(ErrorType.nullValue)
}
}
//MARK:-------Downloder Default Session--------//
lazy var thumbDefaultSession : URLSession = {
//appSessionDefaultConfige.sessionSendsLaunchEvents = true; //-------Edited By Subhra-------------//
//appSessionDefaultConfige.isDiscretionary = true;
let session = URLSession(configuration: appSessionDefaultConfige, delegate: self , delegateQueue: nil) //OperationQueue.main
session.sessionDescription = self.hashIdentifier
return session
}()
//MARK:------Execute operation-------//
override func execute(){
super.execute()
self.inProgress = true
self.fileThumbnailResponseData = Data()
let arcCommon = ARCCommon()
if let filePath = self.storeFilePath ,!arcCommon.isPDFValidAtPath(filePath) || !arcCommon.isFileExistAt(filePath) {
self.inProgress = true
self.expectedContentLength = -1
self.progressContentLength = 0
guard let serviceURL = self.url else { return }
let _token = ARCPreference.shared().authToken() ?? ""
let requestHeader : [String : String] = [head_token : _token,
head_moduleId : "PWP",
content_type : "application/json"]
let request : NSMutableURLRequest = NSMutableURLRequest(url: serviceURL, cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: SROperationConstant.timeIntervalForRequest);
request.httpMethod = "POST"
request.allHTTPHeaderFields = requestHeader
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("gzip", forHTTPHeaderField: "Accept-Encoding")
let urlRequest : URLRequest = request as URLRequest
let session = self.thumbDefaultSession
if let taskIdentifier = self.hashIdentifier, taskIdentifier.isEqual(session.sessionDescription){
self.dataTask = session.dataTask(with: urlRequest)
self.dataTask?.resume()
}else{
self.cleanupThumbConnection()
}
}else{
}
}
//MARK:------Download Thumbnails------//
func downloadThumbnails() -> Void{
guard let thumburl = self.thumbnailDownloadURL else{
print("\n\n Fail presigned requst: \(String(describing: self.fileName)) \n Download Url: \(String(describing: self.thumbnailDownloadURL)) \n hashIdentifier: \(String(describing: self.hashIdentifier))")
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self , closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
return
}
if let _dataTask = self.dataTask{
_dataTask.cancel()
}
let thumbRequest = URLRequest(url: thumburl, cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: SROperationConstant.timeIntervalForRequest)
let session = self.thumbDefaultSession
if let taskIdentifier = self.hashIdentifier, taskIdentifier.isEqual(session.sessionDescription){
self.downloadTask = session.downloadTask(with: thumbRequest)
guard let thumbnailDownloadtask = self.downloadTask else{
return
}
thumbnailDownloadtask.resume()
}else{
self.cleanupThumbConnection()
}
}
//MARK:--------Finished Operation---------//
override func finished(_ errors: [NSError]?) {
super.finished(errors)
}
//MARK:--------Cancel Operation---------//
override func cancel() {
super.cancel()
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self ,closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
}
//MARK:-----Clean Up Task-------//
func cleanupThumbConnection() -> Void{
if let thumbnailDownloadtask = self.downloadTask {
thumbnailDownloadtask.cancel()
}
if let _dataTask = self.dataTask{
_dataTask.cancel()
}
self.inProgress = false
self.finished([])
//self.thumbDefaultSession.invalidateAndCancel()
self.queueManagerDelegate?.removeOperationFromArrayWith(self)
}
deinit {
print("ThumbnailGenerator Dealloc")
}
}
extension SPThumbnailGenerator:URLSessionDataDelegate,URLSessionDownloadDelegate{
/*func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let authMethod = challenge.protectionSpace.authenticationMethod
switch authMethod {
case NSURLAuthenticationMethodServerTrust:
if let serverTrust = challenge.protectionSpace.serverTrust{
var secResult = SecTrustResultType.invalid
let status : OSStatus = SecTrustEvaluate(serverTrust, &secResult)
switch status {
case errSecSuccess:
print("\(SecTrustGetCertificateCount(serverTrust))")
if let serverTrustCertificate : SecCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0){
// Public key pinning
if #available(iOS 10.3, *) {
if let serverHashPublicKey : SecKey = SecCertificateCopyPublicKey(serverTrustCertificate){
let serverPublicKeyData : Data = SecKeyCopyExternalRepresentation(serverHashPublicKey, nil)! as Data
let keyHash = ARCCommon().sha256(data: serverPublicKeyData as Data)
print("\(keyHash)")
if (keyHash == KpinnedPublicKeyHash) {
// Success! This is our server
completionHandler(.useCredential, URLCredential(trust:serverTrust))
return
}
}
} else {
// Fallback on earlier versions
if let serverHashPublicKey : String = SecCertificateCopySubjectSummary(serverTrustCertificate) as String?{
var serverPublicKeyData : Data?
if #available(iOS 10.0, *) {
serverPublicKeyData = SecKeyCopyExternalRepresentation(serverHashPublicKey as! SecKey, nil)! as Data
} else {
// Fallback on earlier versions
//serverPublicKeyData =
}
let keyHash = ARCCommon().sha256(data: serverPublicKeyData!)
if (keyHash == KpinnedPublicKeyHash) {
// Success! This is our server
completionHandler(.useCredential, URLCredential(trust:serverTrust))
return
}
}
}
}
default:
print("")
}
}
default:
print("")
}
// Pinning failed
completionHandler(.cancelAuthenticationChallenge, nil)
}*/
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Swift.Void){
if(dataTask.state == URLSessionTask.State.canceling) {
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self ,closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
completionHandler(.cancel)
return
}else{
if response is HTTPURLResponse {
if let urlResponse : HTTPURLResponse = response as? HTTPURLResponse{
let httpsResponse : HTTPURLResponse = urlResponse
let statusCode = httpsResponse.statusCode
let responseHeader = httpsResponse.allHeaderFields
print("\(responseHeader)")
if statusCode == 200 {
guard let _ = self.saveFilePath else{
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self ,closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
completionHandler(.cancel)
return
}
if self.expectedContentLength == -1 {
self.expectedContentLength = response.expectedContentLength
} else {
}
self.queueManagerDelegate?.operationDidStart(self , closure: { [weak self] _ , _ in
print("Start : \(String(describing: self?.hashIdentifier))")
})
completionHandler(.allow)
}else if statusCode >= 400{
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self ,closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
completionHandler(.cancel)
}
}
}else{
self.expectedContentLength = -1
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self ,closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
completionHandler(.cancel)
}
}
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data){
if(dataTask.state == URLSessionTask.State.canceling) {
return
}else{
guard let _ = self.thumbnailDownloadURL else{
self.fileThumbnailResponseData?.append(data)
return
}
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64){
print("Document Thumb Progress...")
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Swift.Void){
if proposedResponse.response.url?.scheme == "https" {
let updatedResponse = CachedURLResponse(response: proposedResponse.response,
data: proposedResponse.data,
userInfo: proposedResponse.userInfo,
storagePolicy: .allowed)
completionHandler(updatedResponse)
} else {
completionHandler(proposedResponse)
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL){
// Check if the operation has been cancelled
if(downloadTask.state == URLSessionTask.State.canceling) {
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self ,closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
return
}
// Check if the operation has been Suspended
else if(downloadTask.state == URLSessionTask.State.suspended) {
//downloadTask.suspend()
//self.cleanupThumbConnection()
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self ,closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
return
}
let data = try! Data(contentsOf: location)
//self.multiThreadFileWriter?.write(data, toFileOffset: self.progressContentLength)
//self.multiThreadFileWriter?.write(data, toFileOffset: self.progressContentLength, completion: { [unowned self] _ in
guard let filePath = self.saveFilePath else {
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self ,closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
return
}
self.progressContentLength = self.progressContentLength + Int64(data.count)
let fileManager : FileManager = FileManager.default
if fileManager.fileExists(atPath: filePath){
}else{
let savePathURL = URL(fileURLWithPath: filePath)
do{
try fileManager.moveItem(at: location, to: savePathURL)
}catch let error{
print("\(error.localizedDescription)")
print("Can't move download file")
}
}
//-------------//
if self.progressContentLength.hashValue > 0 {
// print("\n\n Finish \n Download Url: \(String(describing: self.thumbnailDownloadURL)) \n hashIdentifier: \(String(describing: self.hashIdentifier))")
self.queueManagerDelegate?.operationDidFinish(self, errors: [] , closure: { [weak self] (_,_) in
let thumbnailId : String = self?.hashIdentifier ?? ""
print("Finish : \(String(describing: thumbnailId))")
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: thumbnailId)
}
self?.cleanupThumbConnection()
})
}else{
print("\n\n Fail presigned requst: \(String(describing: self.fileName)) \n Download Url: \(String(describing: self.thumbnailDownloadURL)) \n hashIdentifier: \(String(describing: self.hashIdentifier))")
self.queueManagerDelegate?.operationDidFail(self , closure: { [weak self] (_ , _) in
let thumbnailId : String = self?.hashIdentifier ?? ""
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: thumbnailId)
}
self?.cleanupThumbConnection()
})
}
}
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64){
}
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?){
// Check if the operation has been cancelled
if(task.state == URLSessionTask.State.canceling){
let thumbnailId : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self, closure: { [weak self] _, _ in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: thumbnailId)
}
self?.cleanupThumbConnection()
})
return
}
// Check if the operation has been Suspended
else if(task.state == URLSessionTask.State.suspended) {
// task.suspend()
let thumbnailId : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self, closure: { [weak self] _, _ in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: thumbnailId)
}
self?.cleanupThumbConnection()
})
return
}
if let errorInfo = error{
print("Thumb Download completed with error: \(String(describing: errorInfo.localizedDescription))")
print("\(String(describing: self.thumbnailDownloadURL))")
self.queueManagerDelegate?.operationDidFail(self, closure: { [weak self] _, _ in
let thumbnailId : String = self?.hashIdentifier ?? ""
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: thumbnailId)
}
self?.cleanupThumbConnection()
})
}else{
guard let _ = self.thumbnailDownloadURL else{
if let data = self.fileThumbnailResponseData{
let response : String? = String(data: data, encoding: .utf8)
let thumbnailURL = response?.replacingOccurrences(of: "\"", with: "")
if let flatMapThumbURL = thumbnailURL{
self.thumbnailDownloadURL = URL(string: flatMapThumbURL)
self.downloadThumbnails()
}else{
print("\n\n Fail presigned requst: \(String(describing: self.fileName)) \n Download Url: \(String(describing: self.thumbnailDownloadURL)) \n hashIdentifier: \(String(describing: self.hashIdentifier))")
let identifier : String = self.hashIdentifier ?? ""
self.queueManagerDelegate?.operationDidFail(self ,closure: { [weak self] (_ , _) in
DispatchQueue.main.async {
NotificationCenter.default.post(name: NSNotification.Name(DOWNLOAD_END), object: identifier)
}
self?.cleanupThumbConnection()
})
}
}
return
}
}
}
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?){
print("Session to invalidate : \(session)")
print("Session Operation Count : \(session.delegateQueue.operations.count)")
}
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession){
print("Session to invalidate : \(session)")
}
}