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
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |