diff --git a/Prey/Classes/AppDelegate.swift b/Prey/Classes/AppDelegate.swift index bf577671..254a059f 100644 --- a/Prey/Classes/AppDelegate.swift +++ b/Prey/Classes/AppDelegate.swift @@ -88,6 +88,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if PreyConfig.sharedInstance.isRegistered { PreyNotification.sharedInstance.registerForRemoteNotifications() TriggerManager.sharedInstance.checkTriggers() + RequestCacheManager.sharedInstance.sendRequest() } else { PreyDeployment.sharedInstance.runPreyDeployment() } diff --git a/Prey/Classes/PreyCoreData.swift b/Prey/Classes/PreyCoreData.swift index f94fac0b..01600042 100644 --- a/Prey/Classes/PreyCoreData.swift +++ b/Prey/Classes/PreyCoreData.swift @@ -103,4 +103,27 @@ class PreyCoreData { return fetchedObjects } + + // Get current RequestCache + func getCurrentRequestCache() -> [RequestCache] { + + var fetchedObjects = [RequestCache]() + let fetchRequest:NSFetchRequest = NSFetchRequest() + + guard let entity = NSEntityDescription.entity(forEntityName: "RequestCache", in:managedObjectContext) else { + return fetchedObjects + } + + fetchRequest.entity = entity + + do { + if let context = managedObjectContext { + fetchedObjects = try context.fetch(fetchRequest) as! [RequestCache] + } + } catch let error as NSError { + PreyLogger("CoreData requestCache error: \(error.localizedDescription)") + } + + return fetchedObjects + } } diff --git a/Prey/Classes/PreyHTTPClient.swift b/Prey/Classes/PreyHTTPClient.swift index 2f98681c..65c1f9cb 100644 --- a/Prey/Classes/PreyHTTPClient.swift +++ b/Prey/Classes/PreyHTTPClient.swift @@ -259,6 +259,22 @@ class PreyHTTPClient : NSObject, URLSessionDataDelegate, URLSessionTaskDelegate } } + // Save on CoreData requests failed + if let err = error, (err as NSError).domain == NSURLErrorDomain, let req = task.originalRequest, let reqUrl = req.url { + // check endpoints + if reqUrl.absoluteString == (URLControlPanel+locationAwareEndpoint) || reqUrl.absoluteString == (URLControlPanel+dataDeviceEndpoint) { + + // Save request + RequestCacheManager.sharedInstance.saveRequest(session.configuration, req, err) + // Delete value for sessionKey + self.requestData.removeValue(forKey:session) + self.requestCompletionHandler.removeValue(forKey:session) + // Cancel session + session.invalidateAndCancel() + return + } + } + DispatchQueue.main.async { // Go to completionHandler if let onCompletion = self.requestCompletionHandler[session] { diff --git a/Prey/Classes/RequestCacheManager.swift b/Prey/Classes/RequestCacheManager.swift index 02fecc32..5d785315 100644 --- a/Prey/Classes/RequestCacheManager.swift +++ b/Prey/Classes/RequestCacheManager.swift @@ -17,5 +17,84 @@ class RequestCacheManager:NSObject { override fileprivate init() { } + func saveRequest(_ sessionConfig: URLSessionConfiguration, _ request: URLRequest, _ error: Error) { + + // Init NSManagedObject type RequestCache + let requestCache = NSEntityDescription.insertNewObject(forEntityName: "RequestCache", into: PreyCoreData.sharedInstance.managedObjectContext) + + // Check timestamp + var req = request + var requestTimestamp = CFAbsoluteTimeGetCurrent() + if let reqHeader = request.allHTTPHeaderFields, let reqTimestamp = reqHeader["Prey-timestamp"], let timestamp = Double(reqTimestamp) { + requestTimestamp = timestamp + } else { + req.addValue(String(requestTimestamp), forHTTPHeaderField:"Prey-timestamp") + } + + // Set values on NSManageObject + let reqData: Data = NSKeyedArchiver.archivedData(withRootObject: req) + requestCache.setValue(reqData, forKey: "request") + + let sessionConfigData: Data = NSKeyedArchiver.archivedData(withRootObject: sessionConfig) + requestCache.setValue(sessionConfigData, forKey: "session_config") + + let errorData: Data = NSKeyedArchiver.archivedData(withRootObject: error) + requestCache.setValue(errorData, forKey: "error") + + requestCache.setValue(requestTimestamp , forKey: "timestamp") + + // Pending check requestCompletionHandler.count when > 2 and 1 fail 1 sucess ?? + if PreyHTTPClient.sharedInstance.requestCompletionHandler.count == 1 { + // Save CoreData + do { + try PreyCoreData.sharedInstance.managedObjectContext.save() + } catch { + PreyLogger("Couldn't save: \(error)") + } + } + } + func sendRequest() { + let requestCacheArray = PreyCoreData.sharedInstance.getCurrentRequestCache() + guard let context = PreyCoreData.sharedInstance.managedObjectContext else {return} + + // Send requests + for req in requestCacheArray { + guard let request = req.request, let sessionConfig = req.session_config, let error = req.error, let time = req.timestamp else { + context.delete(req) + return + } + + guard let decodedRequest = NSKeyedUnarchiver.unarchiveObject(with: request) as? URLRequest, let decodedSession = NSKeyedUnarchiver.unarchiveObject(with: sessionConfig) as? URLSessionConfiguration, let decodedError = NSKeyedUnarchiver.unarchiveObject(with: error) as? Error else { + context.delete(req) + return + } + + // Check timestamp + guard (time.doubleValue + 60*60*24) > CFAbsoluteTimeGetCurrent() else { + PreyConfig.sharedInstance.reportError(decodedError) + context.delete(req) + return + } + + // Resend requests + let session = URLSession(configuration:decodedSession, delegate:PreyHTTPClient.sharedInstance, delegateQueue:nil) + + // Add onCompletion to array + let onCompletion = PreyHTTPResponse.checkResponse(RequestType.dataSend, preyAction:nil, onCompletion:{(isSuccess: Bool) in PreyLogger("Request dataSend")}) + PreyHTTPClient.sharedInstance.requestCompletionHandler.updateValue(onCompletion, forKey: session) + + // Prepare request + PreyHTTPClient.sharedInstance.sendRequest(session, request: decodedRequest) + + context.delete(req) + } + + // Save CoreData + do { + try context.save() + } catch { + PreyLogger("Couldn't save: \(error)") + } + } }