Ios 使用iCloud进行应用程序数据库备份,但不同步
我正在寻求以有限的方式使用iCloud的建议。文档主要关注多个设备之间的同步,我不需要这些。当我说iCloud时,我也包括iCloud Drive,如果这很重要的话 我的iPhone应用程序将其数据和状态存储在一个SQLite数据库文件中,我希望将其保存到iCloud(或iCloud Drive)中以用于备份,而不是在多个设备之间同步更改。在启动时,SQLite打开数据库文件并同步使用它(我使用的是FMDB)。数据库文件应该保留在设备上,这样,如果iCloud关闭,SQLite就不会知道或在意。iCloud会有一个卷影副本 iCloud副本并不总是最新的——我可以通过编程启动对iCloud的更新,并在必要时进行恢复。如果本地数据库文件损坏,或者用户删除了应用程序,我会从iCloud恢复该文件。该文件通常小于1MB,但可能很大(100MB),但大部分是不变的 我不同步是因为共享整个SQLite文件存在数据完整性风险,而且我无法转换为核心数据(这是一个使用FMDB的大型应用程序) 我知道iOS每天都会备份到iCloud,如果我可以从iCloud(而不是整个设备)恢复我的应用程序,那就足够了 我需要关于使用iCloud进行卷影备份和恢复,但不同步的建议 更新:我在这个问题上取得了进展;我可以在iCloud中保存和恢复我真正的SQLite文件。我将Ios 使用iCloud进行应用程序数据库备份,但不同步,ios,sqlite,backup,icloud,fmdb,Ios,Sqlite,Backup,Icloud,Fmdb,我正在寻求以有限的方式使用iCloud的建议。文档主要关注多个设备之间的同步,我不需要这些。当我说iCloud时,我也包括iCloud Drive,如果这很重要的话 我的iPhone应用程序将其数据和状态存储在一个SQLite数据库文件中,我希望将其保存到iCloud(或iCloud Drive)中以用于备份,而不是在多个设备之间同步更改。在启动时,SQLite打开数据库文件并同步使用它(我使用的是FMDB)。数据库文件应该保留在设备上,这样,如果iCloud关闭,SQLite就不会知道或在意。
UIDocument
分为以下两个子类:
@implementation MyDocument
// Override this method to return the document data to be saved.
- (id)contentsForType:(NSString *)typeName error:(NSError * _Nullable *)outError
{
NSString *databasePath = ... // path to my SQLite file in Documents;
NSData *dbData = [NSData dataWithContentsOfFile: databasePath];
return dbData; // the entire database file
}
// Override this method to load the document data into the app’s data model.
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError * _Nullable *)outError
{
NSString *databasePath = ... // path to my SQLite file in Documents;
NSData *dbData = contents;
BOOL ret = [dbData writeToFile: databasePath atomically:YES];
return YES;
}
@end
我的SQLite数据库文件保存在文档中,但我将卷影副本写入iCloud:
- (void)sendToICloud
{
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSString *iCloudFileName = @"..."; // a file name I choose
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:@"Documents"]
URLByAppendingPathComponent:iCloudFileName];
MyDocument *myDoc = [[MyDocument alloc] initWithFileURL:ubiquitousPackage];
[myDoc saveToURL:[myDoc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if ( success ) {
[myDoc closeWithCompletionHandler:^(BOOL success) {
}];
} else {
NSLog(@"iCloud write Syncing FAILED with iCloud");
}
}];
}
现在我的问题是:
MyDocument
(上面是我的iCloudFileName
)使用一个新文件名,我意识到整个新文件将被发送到iCloud。但是,如果我重复使用以前的文件名并每次发送更新的NSData(就像我在loadFromContents:ofType:error:
中所做的那样),iOS是否只发送自上次保存文件以来已更改的部分现在的另一个主题是:使用iCloud跨多个设备进行文件备份 我已经解决了我的主要问题,即查看由多个设备发送的iCloud数据。根据WWDC 2015“构建基于文档的应用程序”,要查看所有文件(甚至来自其他设备的文件),请使用NSMetadataQuery,而不是NSFileManager。此外,视频演示文稿还包含一条关于使用NSURL的警告,因为文档可能会被移动(使用书签),但这不会影响我的用例 我仍然不知道发送部分更改的NSData是否会导致增量更新 我的UIDocument覆盖和sendToICloud方法保持不变。最新的是我的iCloud搜索 如果未写入默认的iCloud Documents目录,则如果该目录不存在,写入将失败。 每次创建它都会更容易,因此:
- (void)writeToiCloud
{
// ...
#define UBIQUITY_ID @"iCloud.TEAMID.bundleID.appName"
NSFileManager *defaultManager = [NSFileManager defaultManager];
NSURL *ubiq = [defaultManager URLForUbiquityContainerIdentifier:UBIQUITY_ID];
NSURL *ubiquitousFolder = [ubiq URLByAppendingPathComponent:@"MyFolder"];
[defaultManager createDirectoryAtURL:ubiquitousFolder
withIntermediateDirectories:YES
attributes:nil error:nil];
// ...
// ... forSaveOperation:UIDocumentSaveForCreating ...
}
- (void)updateFileList
{
// Use an iVar so query stays in scope
// Instance variables maintain a strong reference to the objects by default
query = [[NSMetadataQuery alloc] init];
[query setSearchScopes:@[ // use both for testing, then only DocumentsScope
NSMetadataQueryUbiquitousDataScope, // not in Documents
NSMetadataQueryUbiquitousDocumentsScope, // in Documents
]
];
// add a predicate if desired to restrict the search.
// No predicate finds everything, but Apple says:
// a query can’t be started if ... no predicate has been specified.
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"%K like '*'", NSMetadataItemFSNameKey];
// NSPredicate *predicate = [NSPredicate predicateWithFormat: @"%K CONTAINS %@", NSMetadataItemPathKey, @"/MyFolder/"];
// NSPredicate *predicate = [NSPredicate predicateWithFormat: @"%K CONTAINS %@", NSMetadataItemPathKey, @"/Documents/"];
[query setPredicate:predicate];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(queryDidFinish:)
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
[query startQuery];
}
- (void)queryDidFinish:(NSNotification *)notification
{
// iVar: NSMetadataQuery *query
for ( NSMetadataItem *item in [query results] ) {
NSURL *itemURL = [item valueForAttribute:NSMetadataItemURLKey];
NSLog(@"fileName: %@",itemURL.lastPathComponent);
// can't use getResourceValue:forKey:error
// that applies to URLs that represent file system resources.
if ( [itemURL.absoluteString hasSuffix:@"/"] ) {
continue; // skip directories
}
// process the itemURL here ...
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
}
我为Swift 4添加了解决方案,如果您还有任何问题,请告诉我,