Support multiple backups in single iCloud account.
This commit is contained in:
parent
df25301d57
commit
c86518e44c
|
@ -14,6 +14,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@implementation DebugUIBackup
|
||||
|
||||
#pragma mark - Dependencies
|
||||
|
||||
+ (TSAccountManager *)tsAccountManager
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.tsAccountManager);
|
||||
|
||||
return SSKEnvironment.shared.tsAccountManager;
|
||||
}
|
||||
|
||||
#pragma mark - Factory Methods
|
||||
|
||||
- (NSString *)name
|
||||
|
@ -66,15 +75,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
BOOL success = [data writeToFile:filePath atomically:YES];
|
||||
OWSAssertDebug(success);
|
||||
|
||||
NSString *recipientId = self.tsAccountManager.localNumber;
|
||||
[OWSBackupAPI checkCloudKitAccessWithCompletion:^(BOOL hasAccess) {
|
||||
if (hasAccess) {
|
||||
[OWSBackupAPI saveTestFileToCloudWithFileUrl:[NSURL fileURLWithPath:filePath]
|
||||
success:^(NSString *recordName) {
|
||||
// Do nothing, the API method will log for us.
|
||||
}
|
||||
failure:^(NSError *error){
|
||||
// Do nothing, the API method will log for us.
|
||||
}];
|
||||
[OWSBackupAPI saveTestFileToCloudWithRecipientId:recipientId
|
||||
fileUrl:[NSURL fileURLWithPath:filePath]
|
||||
success:^(NSString *recordName) {
|
||||
// Do nothing, the API method will log for us.
|
||||
}
|
||||
failure:^(NSError *error){
|
||||
// Do nothing, the API method will log for us.
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ NSString *const OWSPrimaryStorage_OWSBackupCollection = @"OWSPrimaryStorage_OWSB
|
|||
NSString *const OWSBackup_IsBackupEnabledKey = @"OWSBackup_IsBackupEnabledKey";
|
||||
NSString *const OWSBackup_LastExportSuccessDateKey = @"OWSBackup_LastExportSuccessDateKey";
|
||||
NSString *const OWSBackup_LastExportFailureDateKey = @"OWSBackup_LastExportFailureDateKey";
|
||||
NSString *const OWSBackupErrorDomain = @"OWSBackupErrorDomain";
|
||||
|
||||
NSString *NSStringForBackupExportState(OWSBackupState state)
|
||||
{
|
||||
|
@ -168,6 +169,16 @@ NSString *NSStringForBackupImportState(OWSBackupState state)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) {
|
||||
OWSLogError(@"Can't backup; not registered and ready.");
|
||||
return;
|
||||
}
|
||||
NSString *_Nullable recipientId = self.tsAccountManager.localNumber;
|
||||
if (recipientId.length < 1) {
|
||||
OWSFailDebug(@"Can't backup; missing recipientId.");
|
||||
return;
|
||||
}
|
||||
|
||||
// In development, make sure there's no export or import in progress.
|
||||
[self.backupExportJob cancel];
|
||||
self.backupExportJob = nil;
|
||||
|
@ -176,7 +187,7 @@ NSString *NSStringForBackupImportState(OWSBackupState state)
|
|||
|
||||
_backupExportState = OWSBackupState_InProgress;
|
||||
|
||||
self.backupExportJob = [[OWSBackupExportJob alloc] initWithDelegate:self primaryStorage:self.primaryStorage];
|
||||
self.backupExportJob = [[OWSBackupExportJob alloc] initWithDelegate:self recipientId:recipientId];
|
||||
[self.backupExportJob startAsync];
|
||||
|
||||
[self postDidChangeNotification];
|
||||
|
@ -313,12 +324,22 @@ NSString *NSStringForBackupImportState(OWSBackupState state)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) {
|
||||
OWSLogError(@"Can't backup; not registered and ready.");
|
||||
return;
|
||||
}
|
||||
NSString *_Nullable recipientId = self.tsAccountManager.localNumber;
|
||||
if (recipientId.length < 1) {
|
||||
OWSFailDebug(@"Can't backup; missing recipientId.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start or abort a backup export if neccessary.
|
||||
if (!self.shouldHaveBackupExport && self.backupExportJob) {
|
||||
[self.backupExportJob cancel];
|
||||
self.backupExportJob = nil;
|
||||
} else if (self.shouldHaveBackupExport && !self.backupExportJob) {
|
||||
self.backupExportJob = [[OWSBackupExportJob alloc] initWithDelegate:self primaryStorage:self.primaryStorage];
|
||||
self.backupExportJob = [[OWSBackupExportJob alloc] initWithDelegate:self recipientId:recipientId];
|
||||
[self.backupExportJob startAsync];
|
||||
}
|
||||
|
||||
|
@ -358,8 +379,31 @@ NSString *NSStringForBackupImportState(OWSBackupState state)
|
|||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
[OWSBackupAPI
|
||||
checkForManifestInCloudWithSuccess:^(BOOL value) {
|
||||
void (^failWithUnexpectedError)(void) = ^{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSError *error =
|
||||
[NSError errorWithDomain:OWSBackupErrorDomain
|
||||
code:1
|
||||
userInfo:@{
|
||||
NSLocalizedDescriptionKey : NSLocalizedString(@"BACKUP_UNEXPECTED_ERROR",
|
||||
@"Error shown when backup fails due to an unexpected error.")
|
||||
}];
|
||||
failure(error);
|
||||
});
|
||||
};
|
||||
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) {
|
||||
OWSLogError(@"Can't backup; not registered and ready.");
|
||||
return failWithUnexpectedError();
|
||||
}
|
||||
NSString *_Nullable recipientId = self.tsAccountManager.localNumber;
|
||||
if (recipientId.length < 1) {
|
||||
OWSFailDebug(@"Can't backup; missing recipientId.");
|
||||
return failWithUnexpectedError();
|
||||
}
|
||||
|
||||
[OWSBackupAPI checkForManifestInCloudWithRecipientId:recipientId
|
||||
success:^(BOOL value) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
success(value);
|
||||
});
|
||||
|
@ -376,6 +420,16 @@ NSString *NSStringForBackupImportState(OWSBackupState state)
|
|||
OWSAssertIsOnMainThread();
|
||||
OWSAssertDebug(!self.backupImportJob);
|
||||
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) {
|
||||
OWSLogError(@"Can't restore backup; not registered and ready.");
|
||||
return;
|
||||
}
|
||||
NSString *_Nullable recipientId = self.tsAccountManager.localNumber;
|
||||
if (recipientId.length < 1) {
|
||||
OWSLogError(@"Can't restore backup; missing recipientId.");
|
||||
return;
|
||||
}
|
||||
|
||||
// In development, make sure there's no export or import in progress.
|
||||
[self.backupExportJob cancel];
|
||||
self.backupExportJob = nil;
|
||||
|
@ -384,7 +438,7 @@ NSString *NSStringForBackupImportState(OWSBackupState state)
|
|||
|
||||
_backupImportState = OWSBackupState_InProgress;
|
||||
|
||||
self.backupImportJob = [[OWSBackupImportJob alloc] initWithDelegate:self primaryStorage:self.primaryStorage];
|
||||
self.backupImportJob = [[OWSBackupImportJob alloc] initWithDelegate:self recipientId:recipientId];
|
||||
[self.backupImportJob startAsync];
|
||||
|
||||
[self postDidChangeNotification];
|
||||
|
@ -512,8 +566,18 @@ NSString *NSStringForBackupImportState(OWSBackupState state)
|
|||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
[OWSBackupAPI
|
||||
fetchAllRecordNamesWithSuccess:^(NSArray<NSString *> *recordNames) {
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) {
|
||||
OWSLogError(@"Can't interact with backup; not registered and ready.");
|
||||
return;
|
||||
}
|
||||
NSString *_Nullable recipientId = self.tsAccountManager.localNumber;
|
||||
if (recipientId.length < 1) {
|
||||
OWSLogError(@"Can't interact with backup; missing recipientId.");
|
||||
return;
|
||||
}
|
||||
|
||||
[OWSBackupAPI fetchAllRecordNamesWithRecipientId:recipientId
|
||||
success:^(NSArray<NSString *> *recordNames) {
|
||||
for (NSString *recordName in [recordNames sortedArrayUsingSelector:@selector(compare:)]) {
|
||||
OWSLogInfo(@"\t %@", recordName);
|
||||
}
|
||||
|
@ -530,8 +594,18 @@ NSString *NSStringForBackupImportState(OWSBackupState state)
|
|||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
[OWSBackupAPI
|
||||
fetchAllRecordNamesWithSuccess:^(NSArray<NSString *> *recordNames) {
|
||||
if (!self.tsAccountManager.isRegisteredAndReady) {
|
||||
OWSLogError(@"Can't interact with backup; not registered and ready.");
|
||||
return;
|
||||
}
|
||||
NSString *_Nullable recipientId = self.tsAccountManager.localNumber;
|
||||
if (recipientId.length < 1) {
|
||||
OWSLogError(@"Can't interact with backup; missing recipientId.");
|
||||
return;
|
||||
}
|
||||
|
||||
[OWSBackupAPI fetchAllRecordNamesWithRecipientId:recipientId
|
||||
success:^(NSArray<NSString *> *recordNames) {
|
||||
if (recordNames.count < 1) {
|
||||
OWSLogInfo(@"No CloudKit records found to clear.");
|
||||
return;
|
||||
|
|
|
@ -19,7 +19,6 @@ import CloudKit
|
|||
//
|
||||
// TODO: Change the record types when we ship to production.
|
||||
static let signalBackupRecordType = "signalBackup"
|
||||
static let manifestRecordName = "manifest"
|
||||
static let payloadKey = "payload"
|
||||
static let maxRetries = 5
|
||||
|
||||
|
@ -42,11 +41,13 @@ import CloudKit
|
|||
// MARK: - Upload
|
||||
|
||||
@objc
|
||||
public class func saveTestFileToCloud(fileUrl: URL,
|
||||
public class func saveTestFileToCloud(recipientId: String,
|
||||
fileUrl: URL,
|
||||
success: @escaping (String) -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
let recordName = "\(recordNamePrefix(forRecipientId: recipientId))test-\(NSUUID().uuidString)"
|
||||
saveFileToCloud(fileUrl: fileUrl,
|
||||
recordName: NSUUID().uuidString,
|
||||
recordName: recordName,
|
||||
recordType: signalBackupRecordType,
|
||||
success: success,
|
||||
failure: failure)
|
||||
|
@ -57,11 +58,13 @@ import CloudKit
|
|||
// We wouldn't want to overwrite previous images until the entire backup export is
|
||||
// complete.
|
||||
@objc
|
||||
public class func saveEphemeralDatabaseFileToCloud(fileUrl: URL,
|
||||
public class func saveEphemeralDatabaseFileToCloud(recipientId: String,
|
||||
fileUrl: URL,
|
||||
success: @escaping (String) -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
let recordName = "\(recordNamePrefix(forRecipientId: recipientId))ephemeralFile-\(NSUUID().uuidString)"
|
||||
saveFileToCloud(fileUrl: fileUrl,
|
||||
recordName: "ephemeralFile-\(NSUUID().uuidString)",
|
||||
recordName: recordName,
|
||||
recordType: signalBackupRecordType,
|
||||
success: success,
|
||||
failure: failure)
|
||||
|
@ -71,19 +74,65 @@ import CloudKit
|
|||
// once. For example, attachment files should only be uploaded once. Subsequent
|
||||
// backups can reuse the same record.
|
||||
@objc
|
||||
public class func recordNameForPersistentFile(fileId: String) -> String {
|
||||
return "persistentFile-\(fileId)"
|
||||
public class func recordNameForPersistentFile(recipientId: String,
|
||||
fileId: String) -> String {
|
||||
return "\(recordNamePrefix(forRecipientId: recipientId))persistentFile-\(fileId)"
|
||||
}
|
||||
|
||||
// "Persistent" files may be shared between backup export; they should only be saved
|
||||
// once. For example, attachment files should only be uploaded once. Subsequent
|
||||
// backups can reuse the same record.
|
||||
@objc
|
||||
public class func savePersistentFileOnceToCloud(fileId: String,
|
||||
public class func recordNameForManifest(recipientId: String) -> String {
|
||||
return "\(recordNamePrefix(forRecipientId: recipientId))manifest"
|
||||
}
|
||||
|
||||
private class func recordNamePrefix(forRecipientId recipientId: String) -> String {
|
||||
return "\(recipientId)-"
|
||||
}
|
||||
|
||||
private class func recipientId(forRecordName recordName: String) -> String? {
|
||||
let recipientIds = self.recipientIds(forRecordNames: [recordName])
|
||||
guard let recipientId = recipientIds.first else {
|
||||
return nil
|
||||
}
|
||||
return recipientId
|
||||
}
|
||||
|
||||
private class func recipientIds(forRecordNames recordNames: [String]) -> [String] {
|
||||
let regex: NSRegularExpression
|
||||
do {
|
||||
regex = try NSRegularExpression(pattern: "(\\+[0-9]+)\\-")
|
||||
} catch {
|
||||
Logger.error("couldn't compile regex: \(error)")
|
||||
return []
|
||||
}
|
||||
var recipientIds = [String]()
|
||||
for recordName in recordNames {
|
||||
guard let match = regex.firstMatch(in: recordName, options: [], range: NSRange(location: 0, length: recordName.count)) else {
|
||||
continue
|
||||
}
|
||||
guard match.range.location == 0 else {
|
||||
// Match must be at start of string.
|
||||
continue
|
||||
}
|
||||
let recipientId = (recordName as NSString).substring(with: match.range) as String
|
||||
recipientIds.append(recipientId)
|
||||
}
|
||||
return recipientIds
|
||||
}
|
||||
|
||||
// "Persistent" files may be shared between backup export; they should only be saved
|
||||
// once. For example, attachment files should only be uploaded once. Subsequent
|
||||
// backups can reuse the same record.
|
||||
@objc
|
||||
public class func savePersistentFileOnceToCloud(recipientId: String,
|
||||
fileId: String,
|
||||
fileUrlBlock: @escaping () -> URL?,
|
||||
success: @escaping (String) -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
saveFileOnceToCloud(recordName: recordNameForPersistentFile(fileId: fileId),
|
||||
let recordName = recordNameForPersistentFile(recipientId: recipientId, fileId: fileId)
|
||||
saveFileOnceToCloud(recordName: recordName,
|
||||
recordType: signalBackupRecordType,
|
||||
fileUrlBlock: fileUrlBlock,
|
||||
success: success,
|
||||
|
@ -91,12 +140,14 @@ import CloudKit
|
|||
}
|
||||
|
||||
@objc
|
||||
public class func upsertManifestFileToCloud(fileUrl: URL,
|
||||
public class func upsertManifestFileToCloud(recipientId: String,
|
||||
fileUrl: URL,
|
||||
success: @escaping (String) -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
// We want to use a well-known record id and type for manifest files.
|
||||
let recordName = recordNameForManifest(recipientId: recipientId)
|
||||
upsertFileToCloud(fileUrl: fileUrl,
|
||||
recordName: manifestRecordName,
|
||||
recordName: recordName,
|
||||
recordType: signalBackupRecordType,
|
||||
success: success,
|
||||
failure: failure)
|
||||
|
@ -348,10 +399,12 @@ import CloudKit
|
|||
}
|
||||
|
||||
@objc
|
||||
public class func checkForManifestInCloud(success: @escaping (Bool) -> Void,
|
||||
public class func checkForManifestInCloud(recipientId: String,
|
||||
success: @escaping (Bool) -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
|
||||
checkForFileInCloud(recordName: manifestRecordName,
|
||||
let recordName = recordNameForManifest(recipientId: recipientId)
|
||||
checkForFileInCloud(recordName: recordName,
|
||||
remainingRetries: maxRetries,
|
||||
success: { (record) in
|
||||
success(record != nil)
|
||||
|
@ -360,12 +413,14 @@ import CloudKit
|
|||
}
|
||||
|
||||
@objc
|
||||
public class func fetchAllRecordNames(success: @escaping ([String]) -> Void,
|
||||
public class func fetchAllRecordNames(recipientId: String,
|
||||
success: @escaping ([String]) -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
|
||||
let query = CKQuery(recordType: signalBackupRecordType, predicate: NSPredicate(value: true))
|
||||
// Fetch the first page of results for this query.
|
||||
fetchAllRecordNamesStep(query: query,
|
||||
fetchAllRecordNamesStep(recipientId: recipientId,
|
||||
query: query,
|
||||
previousRecordNames: [String](),
|
||||
cursor: nil,
|
||||
remainingRetries: maxRetries,
|
||||
|
@ -373,7 +428,30 @@ import CloudKit
|
|||
failure: failure)
|
||||
}
|
||||
|
||||
private class func fetchAllRecordNamesStep(query: CKQuery,
|
||||
@objc
|
||||
public class func fetchAllBackupRecipientIds(success: @escaping ([String]) -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
|
||||
let processResults = { (recordNames: [String]) in
|
||||
DispatchQueue.global().async {
|
||||
let recipientIds = self.recipientIds(forRecordNames: recordNames)
|
||||
success(recipientIds)
|
||||
}
|
||||
}
|
||||
|
||||
let query = CKQuery(recordType: signalBackupRecordType, predicate: NSPredicate(value: true))
|
||||
// Fetch the first page of results for this query.
|
||||
fetchAllRecordNamesStep(recipientId: nil,
|
||||
query: query,
|
||||
previousRecordNames: [String](),
|
||||
cursor: nil,
|
||||
remainingRetries: maxRetries,
|
||||
success: processResults,
|
||||
failure: failure)
|
||||
}
|
||||
|
||||
private class func fetchAllRecordNamesStep(recipientId: String?,
|
||||
query: CKQuery,
|
||||
previousRecordNames: [String],
|
||||
cursor: CKQueryCursor?,
|
||||
remainingRetries: Int,
|
||||
|
@ -390,7 +468,18 @@ import CloudKit
|
|||
queryOperation.desiredKeys = []
|
||||
queryOperation.recordFetchedBlock = { (record) in
|
||||
assert(record.recordID.recordName.count > 0)
|
||||
allRecordNames.append(record.recordID.recordName)
|
||||
|
||||
let recordName = record.recordID.recordName
|
||||
|
||||
if let recipientId = recipientId {
|
||||
let prefix = recordNamePrefix(forRecipientId: recipientId)
|
||||
guard recordName.hasPrefix(prefix) else {
|
||||
Logger.info("Ignoring record: \(recordName)")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
allRecordNames.append(recordName)
|
||||
}
|
||||
queryOperation.queryCompletionBlock = { (cursor, error) in
|
||||
|
||||
|
@ -402,7 +491,8 @@ import CloudKit
|
|||
if let cursor = cursor {
|
||||
Logger.verbose("fetching more record names \(allRecordNames.count).")
|
||||
// There are more pages of results, continue fetching.
|
||||
fetchAllRecordNamesStep(query: query,
|
||||
fetchAllRecordNamesStep(recipientId: recipientId,
|
||||
query: query,
|
||||
previousRecordNames: allRecordNames,
|
||||
cursor: cursor,
|
||||
remainingRetries: maxRetries,
|
||||
|
@ -416,7 +506,8 @@ import CloudKit
|
|||
failure(outcomeError)
|
||||
case .failureRetryAfterDelay(let retryDelay):
|
||||
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + retryDelay, execute: {
|
||||
fetchAllRecordNamesStep(query: query,
|
||||
fetchAllRecordNamesStep(recipientId: recipientId,
|
||||
query: query,
|
||||
previousRecordNames: allRecordNames,
|
||||
cursor: cursor,
|
||||
remainingRetries: remainingRetries - 1,
|
||||
|
@ -425,7 +516,8 @@ import CloudKit
|
|||
})
|
||||
case .failureRetryWithoutDelay:
|
||||
DispatchQueue.global().async {
|
||||
fetchAllRecordNamesStep(query: query,
|
||||
fetchAllRecordNamesStep(recipientId: recipientId,
|
||||
query: query,
|
||||
previousRecordNames: allRecordNames,
|
||||
cursor: cursor,
|
||||
remainingRetries: remainingRetries - 1,
|
||||
|
@ -443,10 +535,12 @@ import CloudKit
|
|||
// MARK: - Download
|
||||
|
||||
@objc
|
||||
public class func downloadManifestFromCloud(
|
||||
success: @escaping (Data) -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
downloadDataFromCloud(recordName: manifestRecordName,
|
||||
public class func downloadManifestFromCloud(recipientId: String,
|
||||
success: @escaping (Data) -> Void,
|
||||
failure: @escaping (Error) -> Void) {
|
||||
|
||||
let recordName = recordNameForManifest(recipientId: recipientId)
|
||||
downloadDataFromCloud(recordName: recordName,
|
||||
success: success,
|
||||
failure: failure)
|
||||
}
|
||||
|
|
|
@ -310,6 +310,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@implementation OWSBackupExportJob
|
||||
|
||||
#pragma mark - Dependencies
|
||||
|
||||
- (OWSPrimaryStorage *)primaryStorage
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.primaryStorage);
|
||||
|
||||
return SSKEnvironment.shared.primaryStorage;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)startAsync
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
@ -439,8 +450,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSLogVerbose(@"");
|
||||
|
||||
__weak OWSBackupExportJob *weakSelf = self;
|
||||
[OWSBackupAPI
|
||||
fetchAllRecordNamesWithSuccess:^(NSArray<NSString *> *recordNames) {
|
||||
[OWSBackupAPI fetchAllRecordNamesWithRecipientId:self.recipientId
|
||||
success:^(NSArray<NSString *> *recordNames) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
OWSBackupExportJob *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
|
@ -719,7 +730,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
OWSAssertDebug(item.encryptedItem.filePath.length > 0);
|
||||
|
||||
[OWSBackupAPI saveEphemeralDatabaseFileToCloudWithFileUrl:[NSURL fileURLWithPath:item.encryptedItem.filePath]
|
||||
[OWSBackupAPI saveEphemeralDatabaseFileToCloudWithRecipientId:self.recipientId
|
||||
fileUrl:[NSURL fileURLWithPath:item.encryptedItem.filePath]
|
||||
success:^(NSString *recordName) {
|
||||
// Ensure that we continue to work off the main thread.
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
@ -765,7 +777,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// * That we already know the metadata for this fragment (from a previous backup
|
||||
// or restore).
|
||||
// * That this record does in fact exist in our CloudKit database.
|
||||
NSString *lastRecordName = [OWSBackupAPI recordNameForPersistentFileWithFileId:attachmentExport.attachmentId];
|
||||
NSString *lastRecordName =
|
||||
[OWSBackupAPI recordNameForPersistentFileWithRecipientId:self.recipientId
|
||||
fileId:attachmentExport.attachmentId];
|
||||
OWSBackupFragment *_Nullable lastBackupFragment = [OWSBackupFragment fetchObjectWithUniqueID:lastRecordName];
|
||||
if (lastBackupFragment && [self.lastValidRecordNames containsObject:lastRecordName]) {
|
||||
OWSAssertDebug(lastBackupFragment.encryptionKey.length > 0);
|
||||
|
@ -803,7 +817,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSAssertDebug(attachmentExport.encryptedItem);
|
||||
}
|
||||
|
||||
[OWSBackupAPI savePersistentFileOnceToCloudWithFileId:attachmentExport.attachmentId
|
||||
[OWSBackupAPI savePersistentFileOnceToCloudWithRecipientId:self.recipientId
|
||||
fileId:attachmentExport.attachmentId
|
||||
fileUrlBlock:^{
|
||||
if (attachmentExport.encryptedItem.filePath.length < 1) {
|
||||
OWSLogError(@"attachment export missing temp file path");
|
||||
|
@ -882,7 +897,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
__weak OWSBackupExportJob *weakSelf = self;
|
||||
|
||||
[OWSBackupAPI upsertManifestFileToCloudWithFileUrl:[NSURL fileURLWithPath:encryptedItem.filePath]
|
||||
[OWSBackupAPI upsertManifestFileToCloudWithRecipientId:self.recipientId
|
||||
fileUrl:[NSURL fileURLWithPath:encryptedItem.filePath]
|
||||
success:^(NSString *recordName) {
|
||||
// Ensure that we continue to work off the main thread.
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
@ -1036,8 +1052,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
__weak OWSBackupExportJob *weakSelf = self;
|
||||
[OWSBackupAPI
|
||||
fetchAllRecordNamesWithSuccess:^(NSArray<NSString *> *recordNames) {
|
||||
[OWSBackupAPI fetchAllRecordNamesWithRecipientId:self.recipientId
|
||||
success:^(NSArray<NSString *> *recordNames) {
|
||||
// Ensure that we continue to work off the main thread.
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSMutableSet<NSString *> *obsoleteRecordNames = [NSMutableSet new];
|
||||
|
|
|
@ -35,6 +35,17 @@ NSString *const kOWSBackup_ImportDatabaseKeySpec = @"kOWSBackup_ImportDatabaseKe
|
|||
|
||||
@implementation OWSBackupImportJob
|
||||
|
||||
#pragma mark - Dependencies
|
||||
|
||||
- (OWSPrimaryStorage *)primaryStorage
|
||||
{
|
||||
OWSAssertDebug(SSKEnvironment.shared.primaryStorage);
|
||||
|
||||
return SSKEnvironment.shared.primaryStorage;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)startAsync
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
|
|
@ -53,22 +53,20 @@ typedef void (^OWSBackupJobManifestFailure)(NSError *error);
|
|||
|
||||
#pragma mark -
|
||||
|
||||
@class OWSPrimaryStorage;
|
||||
|
||||
@interface OWSBackupJob : NSObject
|
||||
|
||||
@property (nonatomic, weak, readonly) id<OWSBackupJobDelegate> delegate;
|
||||
|
||||
@property (nonatomic, readonly) NSString *recipientId;
|
||||
|
||||
// Indicates that the backup succeeded, failed or was cancelled.
|
||||
@property (atomic, readonly) BOOL isComplete;
|
||||
|
||||
@property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage;
|
||||
|
||||
@property (nonatomic, readonly) NSString *jobTempDirPath;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithDelegate:(id<OWSBackupJobDelegate>)delegate primaryStorage:(OWSPrimaryStorage *)primaryStorage;
|
||||
- (instancetype)initWithDelegate:(id<OWSBackupJobDelegate>)delegate recipientId:(NSString *)recipientId;
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
|
|
|
@ -30,11 +30,11 @@ NSString *const kOWSBackup_KeychainService = @"kOWSBackup_KeychainService";
|
|||
|
||||
@property (nonatomic, weak) id<OWSBackupJobDelegate> delegate;
|
||||
|
||||
@property (nonatomic) NSString *recipientId;
|
||||
|
||||
@property (atomic) BOOL isComplete;
|
||||
@property (atomic) BOOL hasSucceeded;
|
||||
|
||||
@property (nonatomic) OWSPrimaryStorage *primaryStorage;
|
||||
|
||||
@property (nonatomic) NSString *jobTempDirPath;
|
||||
|
||||
@end
|
||||
|
@ -43,7 +43,7 @@ NSString *const kOWSBackup_KeychainService = @"kOWSBackup_KeychainService";
|
|||
|
||||
@implementation OWSBackupJob
|
||||
|
||||
- (instancetype)initWithDelegate:(id<OWSBackupJobDelegate>)delegate primaryStorage:(OWSPrimaryStorage *)primaryStorage
|
||||
- (instancetype)initWithDelegate:(id<OWSBackupJobDelegate>)delegate recipientId:(NSString *)recipientId
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
@ -51,11 +51,11 @@ NSString *const kOWSBackup_KeychainService = @"kOWSBackup_KeychainService";
|
|||
return self;
|
||||
}
|
||||
|
||||
OWSAssertDebug(primaryStorage);
|
||||
OWSAssertDebug(recipientId.length > 0);
|
||||
OWSAssertDebug([OWSStorage isStorageReady]);
|
||||
|
||||
self.delegate = delegate;
|
||||
self.primaryStorage = primaryStorage;
|
||||
self.recipientId = recipientId;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -162,8 +162,8 @@ NSString *const kOWSBackup_KeychainService = @"kOWSBackup_KeychainService";
|
|||
OWSLogVerbose(@"");
|
||||
|
||||
__weak OWSBackupJob *weakSelf = self;
|
||||
[OWSBackupAPI
|
||||
downloadManifestFromCloudWithSuccess:^(NSData *data) {
|
||||
[OWSBackupAPI downloadManifestFromCloudWithRecipientId:self.recipientId
|
||||
success:^(NSData *data) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[weakSelf
|
||||
processManifest:data
|
||||
|
|
Loading…
Reference in New Issue