mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			177 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Swift
		
	
		
		
			
		
	
	
			177 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Swift
		
	
| 
											7 years ago
										 | // | ||
|  | //  Copyright (c) 2018 Open Whisper Systems. All rights reserved. | ||
|  | // | ||
|  | 
 | ||
|  | import Foundation | ||
|  | import PromiseKit | ||
| 
											5 years ago
										 | import SignalUtilitiesKit | ||
| 
											7 years ago
										 | 
 | ||
|  | @objc(OWSBackupLazyRestore) | ||
|  | public class BackupLazyRestore: NSObject { | ||
|  | 
 | ||
|  |     // MARK: - Dependencies | ||
|  | 
 | ||
|  |     private var backup: OWSBackup { | ||
|  |         return AppEnvironment.shared.backup | ||
|  |     } | ||
|  | 
 | ||
|  |     private var primaryStorage: OWSPrimaryStorage { | ||
|  |         return SSKEnvironment.shared.primaryStorage | ||
|  |     } | ||
|  | 
 | ||
|  |     private var tsAccountManager: TSAccountManager { | ||
|  |         return TSAccountManager.sharedInstance() | ||
|  |     } | ||
|  | 
 | ||
|  |     // MARK: - | ||
|  | 
 | ||
|  |     private var isRunning = false | ||
|  |     private var isComplete = false | ||
|  | 
 | ||
|  |     @objc | ||
|  |     public required override init() { | ||
|  |         super.init() | ||
|  | 
 | ||
|  |         SwiftSingletons.register(self) | ||
|  | 
 | ||
|  |         AppReadiness.runNowOrWhenAppDidBecomeReady { | ||
|  |             self.runIfNecessary() | ||
|  |         } | ||
|  | 
 | ||
|  |         NotificationCenter.default.addObserver(forName: .OWSApplicationDidBecomeActive, object: nil, queue: nil) { _ in | ||
|  |             self.runIfNecessary() | ||
|  |         } | ||
|  |         NotificationCenter.default.addObserver(forName: .RegistrationStateDidChange, object: nil, queue: nil) { _ in | ||
|  |             self.runIfNecessary() | ||
|  |         } | ||
|  |         NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { _ in | ||
|  |             self.runIfNecessary() | ||
|  |         } | ||
| 
											7 years ago
										 |         NotificationCenter.default.addObserver(forName: .reachabilityChanged, object: nil, queue: nil) { _ in | ||
|  |             self.runIfNecessary() | ||
|  |         } | ||
|  |         NotificationCenter.default.addObserver(forName: NSNotification.Name(NSNotificationNameBackupStateDidChange), object: nil, queue: nil) { _ in | ||
|  |             self.runIfNecessary() | ||
|  |         } | ||
| 
											7 years ago
										 |     } | ||
|  | 
 | ||
|  |     // MARK: - | ||
|  | 
 | ||
| 
											7 years ago
										 |     private let backgroundQueue = DispatchQueue.global(qos: .background) | ||
| 
											7 years ago
										 | 
 | ||
| 
											7 years ago
										 |     @objc | ||
|  |     public func clearCompleteAndRunIfNecessary() { | ||
|  |         AssertIsOnMainThread() | ||
|  | 
 | ||
|  |         isComplete = false | ||
|  | 
 | ||
|  |         runIfNecessary() | ||
|  |     } | ||
|  | 
 | ||
| 
											7 years ago
										 |     @objc | ||
|  |     public func isBackupImportInProgress() -> Bool { | ||
|  |         return backup.backupImportState == .inProgress | ||
|  |     } | ||
|  | 
 | ||
| 
											7 years ago
										 |     @objc | ||
|  |     public func runIfNecessary() { | ||
|  |         AssertIsOnMainThread() | ||
|  | 
 | ||
| 
											7 years ago
										 |         guard !CurrentAppContext().isRunningTests else { | ||
|  |             return | ||
|  |         } | ||
| 
											7 years ago
										 |         guard AppReadiness.isAppReady() else { | ||
|  |             return | ||
|  |         } | ||
| 
											7 years ago
										 |         guard CurrentAppContext().isMainAppAndActive else { | ||
|  |             return | ||
|  |         } | ||
|  |         guard tsAccountManager.isRegisteredAndReady() else { | ||
|  |             return | ||
|  |         } | ||
| 
											7 years ago
										 |         guard !isBackupImportInProgress() else { | ||
|  |             return | ||
|  |         } | ||
| 
											7 years ago
										 |         guard !isRunning, !isComplete else { | ||
|  |             return | ||
|  |         } | ||
|  | 
 | ||
|  |         isRunning = true | ||
|  | 
 | ||
|  |         backgroundQueue.async { | ||
|  |             self.restoreAttachments() | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     private func restoreAttachments() { | ||
|  |         let temporaryDirectory = OWSTemporaryDirectory() | ||
|  |         let jobTempDirPath = (temporaryDirectory as NSString).appendingPathComponent(NSUUID().uuidString) | ||
|  | 
 | ||
|  |         guard OWSFileSystem.ensureDirectoryExists(jobTempDirPath) else { | ||
|  |             Logger.error("could not create temp directory.") | ||
|  |             complete(errorCount: 1) | ||
|  |             return | ||
|  |         } | ||
|  | 
 | ||
|  |         let backupIO = OWSBackupIO(jobTempDirPath: jobTempDirPath) | ||
|  | 
 | ||
|  |         let attachmentIds = backup.attachmentIdsForLazyRestore() | ||
|  |         guard attachmentIds.count > 0 else { | ||
|  |             Logger.info("No attachments need lazy restore.") | ||
|  |             complete(errorCount: 0) | ||
|  |             return | ||
|  |         } | ||
|  |         Logger.info("Lazy restoring \(attachmentIds.count) attachments.") | ||
|  |         tryToRestoreNextAttachment(attachmentIds: attachmentIds, errorCount: 0, backupIO: backupIO) | ||
|  |     } | ||
|  | 
 | ||
|  |     private func tryToRestoreNextAttachment(attachmentIds: [String], errorCount: UInt, backupIO: OWSBackupIO) { | ||
| 
											7 years ago
										 |         guard !isBackupImportInProgress() else { | ||
|  |             Logger.verbose("A backup import is in progress; abort.") | ||
|  |             complete(errorCount: errorCount + 1) | ||
|  |             return | ||
|  |         } | ||
|  | 
 | ||
| 
											7 years ago
										 |         var attachmentIdsCopy = attachmentIds | ||
| 
											7 years ago
										 |         guard let attachmentId = attachmentIdsCopy.popLast() else { | ||
| 
											7 years ago
										 |             // This job is done. | ||
|  |             Logger.verbose("job is done.") | ||
|  |             complete(errorCount: errorCount) | ||
|  |             return | ||
|  |         } | ||
|  |         guard let attachmentPointer = TSAttachment.fetch(uniqueId: attachmentId) as? TSAttachmentPointer else { | ||
|  |             Logger.warn("could not load attachment.") | ||
|  |             // Not necessarily an error. | ||
|  |             // The attachment might have been deleted since the job began. | ||
|  |             // Continue trying to restore the other attachments. | ||
|  |             tryToRestoreNextAttachment(attachmentIds: attachmentIds, errorCount: errorCount + 1, backupIO: backupIO) | ||
|  |             return | ||
|  |         } | ||
|  |         backup.lazyRestoreAttachment(attachmentPointer, | ||
|  |                                      backupIO: backupIO) | ||
| 
											7 years ago
										 |             .done(on: self.backgroundQueue) { _ in | ||
| 
											7 years ago
										 |                 Logger.info("Restored attachment.") | ||
|  | 
 | ||
| 
											7 years ago
										 |                 // Continue trying to restore the other attachments. | ||
|  |                 self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, errorCount: errorCount, backupIO: backupIO) | ||
|  |             }.catch(on: self.backgroundQueue) { (error) in | ||
| 
											7 years ago
										 |                 Logger.error("Could not restore attachment: \(error)") | ||
|  | 
 | ||
| 
											7 years ago
										 |                 // Continue trying to restore the other attachments. | ||
|  |                 self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, errorCount: errorCount + 1, backupIO: backupIO) | ||
|  |             }.retainUntilComplete() | ||
| 
											7 years ago
										 |     } | ||
|  | 
 | ||
|  |     private func complete(errorCount: UInt) { | ||
|  |         Logger.verbose("") | ||
|  | 
 | ||
|  |         DispatchQueue.main.async { | ||
|  |             self.isRunning = false | ||
|  | 
 | ||
|  |             if errorCount == 0 { | ||
|  |                 self.isComplete = true | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |