|  |  | @ -149,12 +149,79 @@ import CloudKit | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // Compare: | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // * An "upsert" creates a new record if none exists and | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //   or updates if there is an existing record. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // * A "save once" creates a new record if none exists and | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //   does nothing if there is an existing record. | 
			
		
	
		
		
			
				
					
					|  |  |  |     @objc |  |  |  |     @objc | 
			
		
	
		
		
			
				
					
					|  |  |  |     public class func upsertFileToCloud(fileUrl: URL, |  |  |  |     public class func upsertFileToCloud(fileUrl: URL, | 
			
		
	
		
		
			
				
					
					|  |  |  |                                         recordName: String, |  |  |  |                                         recordName: String, | 
			
		
	
		
		
			
				
					
					|  |  |  |                                         recordType: String, |  |  |  |                                         recordType: String, | 
			
		
	
		
		
			
				
					
					|  |  |  |                                         success: @escaping (String) -> Swift.Void, |  |  |  |                                         success: @escaping (String) -> Swift.Void, | 
			
		
	
		
		
			
				
					
					|  |  |  |                                         failure: @escaping (Error) -> Swift.Void) { |  |  |  |                                         failure: @escaping (Error) -> Swift.Void) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         checkForFileInCloud(recordName: recordName, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                             success: { (record) in | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                 if let record = record { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     // Record found, updating existing record. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     let asset = CKAsset(fileURL: fileUrl) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     record[payloadKey] = asset | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     saveRecordToCloud(record: record, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                       success: success, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                       failure: failure) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                 } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     // No record found, saving new record. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     saveFileToCloud(fileUrl: fileUrl, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                     recordName: recordName, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                     recordType: recordType, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                     success: success, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                     failure: failure) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                 } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         }, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                             failure: failure) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // Compare: | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // * An "upsert" creates a new record if none exists and | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //   or updates if there is an existing record. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // * A "save once" creates a new record if none exists and | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     //   does nothing if there is an existing record. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     @objc | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     public class func saveFileOnceToCloud(recordName: String, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                           recordType: String, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                           fileUrlBlock: @escaping (Swift.Void) -> URL?, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                           success: @escaping (String) -> Swift.Void, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                           failure: @escaping (Error) -> Swift.Void) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         checkForFileInCloud(recordName: recordName, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                             success: { (record) in | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                 if record != nil { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     // Record found, skipping save. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     success(recordName) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                 } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     // No record found, saving new record. | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     guard let fileUrl = fileUrlBlock() else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                         Logger.error("\(self.logTag) error preparing file for upload.") | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                         failure(OWSErrorWithCodeDescription(.exportBackupError, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                                             NSLocalizedString("BACKUP_EXPORT_ERROR_SAVE_FILE_TO_CLOUD_FAILED", | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                                                               comment: "Error indicating the a backup export failed to save a file to the cloud."))) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                         return | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                     saveFileToCloud(fileUrl: fileUrl, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                     recordName: recordName, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                     recordType: recordType, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                     success: success, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                                     failure: failure) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                 } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         }, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                             failure: failure) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     private class func checkForFileInCloud(recordName: String, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                           success: @escaping (CKRecord?) -> Swift.Void, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                                           failure: @escaping (Error) -> Swift.Void) { | 
			
		
	
		
		
			
				
					
					|  |  |  |         let recordId = CKRecordID(recordName: recordName) |  |  |  |         let recordId = CKRecordID(recordName: recordName) | 
			
		
	
		
		
			
				
					
					|  |  |  |         let fetchOperation = CKFetchRecordsOperation(recordIDs: [recordId ]) |  |  |  |         let fetchOperation = CKFetchRecordsOperation(recordIDs: [recordId ]) | 
			
		
	
		
		
			
				
					
					|  |  |  |         // Don't download the file; we're just using the fetch to check whether or |  |  |  |         // Don't download the file; we're just using the fetch to check whether or | 
			
		
	
	
		
		
			
				
					|  |  | @ -164,12 +231,8 @@ import CloudKit | 
			
		
	
		
		
			
				
					
					|  |  |  |             if let error = error { |  |  |  |             if let error = error { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if let ckerror = error as? CKError { |  |  |  |                 if let ckerror = error as? CKError { | 
			
		
	
		
		
			
				
					
					|  |  |  |                     if ckerror.code == .unknownItem { |  |  |  |                     if ckerror.code == .unknownItem { | 
			
		
	
		
		
			
				
					
					|  |  |  |                         // No record found to update, saving new record. |  |  |  |                         // Record not found. | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         saveFileToCloud(fileUrl: fileUrl, |  |  |  |                         success(nil) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                         recordName: recordName, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                                         recordType: recordType, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                                         success: success, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                                         failure: failure) |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                         return |  |  |  |                         return | 
			
		
	
		
		
			
				
					
					|  |  |  |                     } |  |  |  |                     } | 
			
		
	
		
		
			
				
					
					|  |  |  |                     Logger.error("\(self.logTag) error fetching record: \(error) \(ckerror.code).") |  |  |  |                     Logger.error("\(self.logTag) error fetching record: \(error) \(ckerror.code).") | 
			
		
	
	
		
		
			
				
					|  |  | @ -180,19 +243,14 @@ import CloudKit | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return |  |  |  |                 return | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |             guard let record = record else { |  |  |  |             guard let record = record else { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 Logger.error("\(self.logTag) error missing record.") |  |  |  |                 Logger.error("\(self.logTag) missing fetching record.") | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 Logger.flush() |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                 failure(OWSErrorWithCodeDescription(.exportBackupError, |  |  |  |                 failure(OWSErrorWithCodeDescription(.exportBackupError, | 
			
		
	
		
		
			
				
					
					|  |  |  |                                                     NSLocalizedString("BACKUP_EXPORT_ERROR_SAVE_FILE_TO_CLOUD_FAILED", |  |  |  |                                                     NSLocalizedString("BACKUP_EXPORT_ERROR_SAVE_FILE_TO_CLOUD_FAILED", | 
			
		
	
		
		
			
				
					
					|  |  |  |                                                                       comment: "Error indicating the a backup export failed to save a file to the cloud."))) |  |  |  |                                                                       comment: "Error indicating the a backup export failed to save a file to the cloud."))) | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return |  |  |  |                 return | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |             Logger.verbose("\(self.logTag) updating record.") |  |  |  |             // Record found. | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             let asset = CKAsset(fileURL: fileUrl) |  |  |  |             success(record) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             record[payloadKey] = asset |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             saveRecordToCloud(record: record, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                               success: success, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                               failure: failure) |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |         let myContainer = CKContainer.default() |  |  |  |         let myContainer = CKContainer.default() | 
			
		
	
		
		
			
				
					
					|  |  |  |         let privateDatabase = myContainer.privateCloudDatabase |  |  |  |         let privateDatabase = myContainer.privateCloudDatabase | 
			
		
	
	
		
		
			
				
					|  |  | @ -200,47 +258,14 @@ import CloudKit | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     @objc |  |  |  |     @objc | 
			
		
	
		
		
			
				
					
					|  |  |  |     public class func saveFileOnceToCloud(recordName: String, |  |  |  |     public class func checkForManifestInCloud(success: @escaping (Bool) -> Swift.Void, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                           recordType: String, |  |  |  |                                               failure: @escaping (Error) -> Swift.Void) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                           fileUrlBlock: @escaping (Swift.Void) -> URL?, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                                           success: @escaping (String) -> Swift.Void, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                                           failure: @escaping (Error) -> Swift.Void) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         let recordId = CKRecordID(recordName: recordName) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         let fetchOperation = CKFetchRecordsOperation(recordIDs: [recordId ]) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         // Don't download the file; we're just using the fetch to check whether or |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         // not this record already exists. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         fetchOperation.desiredKeys = [] |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         fetchOperation.perRecordCompletionBlock = { (record, recordId, error) in |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             if let error = error { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if let ckerror = error as? CKError { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     if ckerror.code == .unknownItem { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                         // No record found to update, saving new record. |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                         guard let fileUrl = fileUrlBlock() else { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                             Logger.error("\(self.logTag) error preparing file for upload.") |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                             return |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                         } |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                         saveFileToCloud(fileUrl: fileUrl, |  |  |  |         checkForFileInCloud(recordName: manifestRecordName, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                         recordName: recordName, |  |  |  |                             success: { (record) in | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                         recordType: recordType, |  |  |  |                                 success(record != nil) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                         success: success, |  |  |  |         }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                                         failure: failure) |  |  |  |                             failure: failure) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         return |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     Logger.error("\(self.logTag) error fetching record: \(error) \(ckerror.code).") |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 } else { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     Logger.error("\(self.logTag) error fetching record: \(error).") |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 failure(error) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 return |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             Logger.info("\(self.logTag) record already exists; skipping save.") |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             success(recordName) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         let myContainer = CKContainer.default() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         let privateDatabase = myContainer.privateCloudDatabase |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         privateDatabase.add(fetchOperation) |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     @objc |  |  |  |     @objc | 
			
		
	
	
		
		
			
				
					|  |  | 
 |