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
		
	
| //
 | |
| //  Copyright (c) 2018 Open Whisper Systems. All rights reserved.
 | |
| //
 | |
| 
 | |
| import Foundation
 | |
| import PromiseKit
 | |
| import SignalUtilitiesKit
 | |
| 
 | |
| @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()
 | |
|         }
 | |
|         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()
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // MARK: -
 | |
| 
 | |
|     private let backgroundQueue = DispatchQueue.global(qos: .background)
 | |
| 
 | |
|     @objc
 | |
|     public func clearCompleteAndRunIfNecessary() {
 | |
|         AssertIsOnMainThread()
 | |
| 
 | |
|         isComplete = false
 | |
| 
 | |
|         runIfNecessary()
 | |
|     }
 | |
| 
 | |
|     @objc
 | |
|     public func isBackupImportInProgress() -> Bool {
 | |
|         return backup.backupImportState == .inProgress
 | |
|     }
 | |
| 
 | |
|     @objc
 | |
|     public func runIfNecessary() {
 | |
|         AssertIsOnMainThread()
 | |
| 
 | |
|         guard !CurrentAppContext().isRunningTests else {
 | |
|             return
 | |
|         }
 | |
|         guard AppReadiness.isAppReady() else {
 | |
|             return
 | |
|         }
 | |
|         guard CurrentAppContext().isMainAppAndActive else {
 | |
|             return
 | |
|         }
 | |
|         guard tsAccountManager.isRegisteredAndReady() else {
 | |
|             return
 | |
|         }
 | |
|         guard !isBackupImportInProgress() else {
 | |
|             return
 | |
|         }
 | |
|         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) {
 | |
|         guard !isBackupImportInProgress() else {
 | |
|             Logger.verbose("A backup import is in progress; abort.")
 | |
|             complete(errorCount: errorCount + 1)
 | |
|             return
 | |
|         }
 | |
| 
 | |
|         var attachmentIdsCopy = attachmentIds
 | |
|         guard let attachmentId = attachmentIdsCopy.popLast() else {
 | |
|             // 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)
 | |
|             .done(on: self.backgroundQueue) { _ in
 | |
|                 Logger.info("Restored attachment.")
 | |
| 
 | |
|                 // Continue trying to restore the other attachments.
 | |
|                 self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, errorCount: errorCount, backupIO: backupIO)
 | |
|             }.catch(on: self.backgroundQueue) { (error) in
 | |
|                 Logger.error("Could not restore attachment: \(error)")
 | |
| 
 | |
|                 // Continue trying to restore the other attachments.
 | |
|                 self.tryToRestoreNextAttachment(attachmentIds: attachmentIdsCopy, errorCount: errorCount + 1, backupIO: backupIO)
 | |
|             }.retainUntilComplete()
 | |
|     }
 | |
| 
 | |
|     private func complete(errorCount: UInt) {
 | |
|         Logger.verbose("")
 | |
| 
 | |
|         DispatchQueue.main.async {
 | |
|             self.isRunning = false
 | |
| 
 | |
|             if errorCount == 0 {
 | |
|                 self.isComplete = true
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |