【发布时间】:2012-11-21 22:36:51
【问题描述】:
我无法从我的应用在一台设备上将简单文件写入 iCloud 容器,然后在第一次我的应用在第二台设备上运行时成功读取该文件。
我在一对新重置的设备上使用空的 Ubiquity Container 开始了这个测试场景。下面是演示我遇到的问题的测试用例代码和控制台日志记录。下面的所有代码都在后台线程上运行。
我在第一台设备上安装并运行测试用例。代码在 ubiquity 容器中查找某个文件,并在找不到时创建它。显然,这是它第一次在第一台设备上运行时的预期。我希望(并期待)当应用程序全新安装并在第二台设备上运行时,它能够找到并读取在第一台设备上创建的文件的内容。似乎在重置设备上新安装的应用程序永远无法找到和读取现有文件,并且总是认为它需要创建一个新文件,如果它已经存在,不幸的是最终替换了 ubiquity 容器中的文件。
但是,在第一次尝试(在第二台设备上)未能找到并读取文件后,如果我删除应用程序,重新安装然后在同一设备上重新运行它,它确实找到了文件并且是能够读取其当前内容。似乎应用程序的第一次运行导致文件被加载到普遍存在的缓存中,但不是处于应用程序可以找到的状态。应用的第二次运行似乎在缓存中找到了文件。
我知道您应该开始在 iOS 设备上下载文件,但在第一次使用设备时,它会报告“找不到文件”。这里还有什么需要做的吗?还是这看起来像是一个新的或已知的错误?
以下代码用于确定令牌文件是否存在于 ubiquity 容器中并确保它可以被读取。
NSError *error;
BOOL rc;
NSFileManager *fm = [[NSFileManager alloc] init];
NSURL *ubiquityURL = [fm URLForUbiquityContainerIdentifier:nil];
NSURL *tokenURL = [ubiquityURL URLByAppendingPathComponent:@"iCloudTokenFile"];
NSLog(@"isTokenFilePresentAtUrl: Entered for Device Name: %@", [[UIDevice currentDevice] name]);
error = nil;
rc = [fm startDownloadingUbiquitousItemAtURL:tokenURL error:&error];
NSLog(@"startDownloadingUbiquitousItemAtURL: %d Error: %@(%d)", rc, error.domain, error.code);
rc = [fm isUbiquitousItemAtURL:tokenURL];
NSLog(@"isUbiquitousItemAtURL: %d", rc);
NSNumber *isUbiquitousItem = nil;
error = nil;
rc = [tokenURL getResourceValue:&isUbiquitousItem forKey:NSURLIsUbiquitousItemKey error:&error];
NSLog(@"getResourceValue: %d isUbiquitousItem: %@ Error: %@(%d)", rc, isUbiquitousItem, error.domain, error.code);
NSNumber *isDownloading = nil;
error = nil;
rc = [tokenURL getResourceValue:&isDownloading forKey:NSURLUbiquitousItemIsDownloadingKey error:&error];
NSLog(@"getResourceValue: %d isDownloading: %@ Error: %@(%d)", rc, isDownloading, error.domain, error.code);
NSNumber *isDownloaded = nil;
error = nil;
rc = [tokenURL getResourceValue:&isDownloaded forKey:NSURLUbiquitousItemIsDownloadedKey error:&error];
NSLog(@"getResourceValue: %d isDownloaded: %@ Error: %@(%d)", rc, isDownloaded, error.domain, error.code);
以下代码用于尝试从 ubiquity 容器中读取令牌文件。
__block BOOL success = NO;
__block NSError *error = nil;
NSFileCoordinator *fc = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[fc coordinateReadingItemAtURL:tokenURL
options:NSFileCoordinatorReadingWithoutChanges
error:&error
byAccessor:^(NSURL *newURL)
{
NSLog(@"readTokenFileFromUrl: Trying to read Token File.");
NSNumber *isDownloaded = nil;
error = nil;
BOOL rc = [tokenURL getResourceValue:&isDownloaded forKey:NSURLUbiquitousItemIsDownloadedKey error:&error];
NSLog(@"getResourceValue: %d isDownloaded: %@ Error: %@(%d)", rc, isDownloaded, error.domain, error.code);
NSString *uuidString = nil;
NSError *blockerror = nil;
uuidString = [NSString stringWithContentsOfURL:newURL
encoding:NSUTF8StringEncoding
error:&blockerror];
NSLog(@"readTokenFileFromUrl: Token read from file: %@", uuidString);
}];
以下代码用于在无法读取现有令牌文件的情况下将令牌文件写入到 ubiquity 容器中。我原以为这段代码只会由第一台设备运行一次。
NSFileCoordinator *fc = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
NSError *error = nil;
[fc coordinateWritingItemAtURL:tokenURL
options:NSFileCoordinatorWritingForReplacing
error:&error
byAccessor:^(NSURL *newURL)
{
NSUUID *uuid = [[NSUUID alloc] init];
NSString *uuidString = [uuid UUIDString];
NSError *blockerror = nil;
NSLog(@"writeTokenFileToUrl: Writing token to file: %@", uuidString);
[uuidString writeToURL:newURL
atomically:YES
encoding:NSUTF8StringEncoding
error:&blockerror];
}];
应用程序在“Red iPod touch”上首次安装并运行。
isTokenFilePresentAtUrl: Entered for Device Name: Red iPod touch
startDownloadingUbiquitousItemAtURL: 0 Error: NSCocoaErrorDomain(4)
isUbiquitousItemAtURL: 0
getResourceValue: 0 isUbiquitousItem: (null) Error: NSPOSIXErrorDomain(2)
getResourceValue: 0 isDownloading: (null) Error: NSPOSIXErrorDomain(2)
getResourceValue: 0 isDownloaded: (null) Error: NSPOSIXErrorDomain(2)
readTokenFileFromUrl: Trying to read Token File.
getResourceValue: 0 isDownloaded: (null) Error: NSPOSIXErrorDomain(2)
readTokenFileFromUrl: Token from File: (null)
readTokenFileFromUrl: Error reading uuid Token from File: NSCocoaErrorDomain(260)
loadiCloudStore: isUbiquitousItemAtURL: Token File actually does not appear to be present.
writeTokenFileToUrl: Writing token to file: 6AAA8D81-4BB5-4BF4-AD31-E152D3BADD13
此时,已通过 developer.icloud.com 网站验证文件是否存在于 Ubiquity 容器中。然后在名为“Blue iPod touch”的第二台设备上安装并首次运行该应用程序。
isTokenFilePresentAtUrl: Entered for Device Name: Blue iPod touch
startDownloadingUbiquitousItemAtURL: 0 Error: NSCocoaErrorDomain(4)
isUbiquitousItemAtURL: 0
getResourceValue: 0 isUbiquitousItem: (null) Error: NSPOSIXErrorDomain(2)
getResourceValue: 0 isDownloading: (null) Error: NSPOSIXErrorDomain(2)
getResourceValue: 0 isDownloaded: (null) Error: NSPOSIXErrorDomain(2)
readTokenFileFromUrl: Trying to read Token File.
getResourceValue: 0 isDownloaded: (null) Error: NSPOSIXErrorDomain(2)
readTokenFileFromUrl: Token from File: (null)
readTokenFileFromUrl: Error reading uuid Token from File: NSCocoaErrorDomain(260)
对于本次测试,App在尝试读取后停止,不允许尝试写入或替换令牌文件。然后该应用程序将被杀死、删除并重新安装并在 Blue iPod touch 上再次运行。
isTokenFilePresentAtUrl: Entered for Device Name: Blue iPod touch
startDownloadingUbiquitousItemAtURL: 1 Error: (null)(0)
isUbiquitousItemAtURL: 1
getResourceValue: 1 isUbiquitousItem: 1 Error: (null)(0)
getResourceValue: 1 isDownloading: 1 Error: (null)(0)
getResourceValue: 1 isDownloaded: 0 Error: (null)(0)
readTokenFileFromUrl: Trying to read Token File.
getResourceValue: 1 isDownloaded: 0 Error: (null)(0)
readTokenFileFromUrl: Token read from file: 6AAA8D81-4BB5-4BF4-AD31-E152D3BADD13
这一次它找到并读取具有预期内容的文件。
【问题讨论】:
-
今天我发现了另一个question,看起来它在问同样的问题。
-
我也观察到几乎完全相同的行为。除了我在第一次查询时在值字段(您的 isDownloaded)中看到(空)响应。创建一个新文件并将其移动到 ubiquity 容器失败并显示 512,但在随后的运行中,它似乎实际上成功并覆盖了真实(来自 iCloud 服务器)数据。
-
我认为我们实际上看到了同样的事情。我在第一台设备上的第一个查询和第二台设备上的第一个查询上得到相同的(空)响应。直到第二台设备上的后续设备,我才看到 isDownloaded 的值。
-
这是一个噩梦般的问题,因为蓝色设备在首次运行时必须相信没有任何数据,并创建一些初始数据。然后这将被 iCloud 视为“最新有效”,并将覆盖有效数据,破坏用户体验。
-
是的,这就是问题所在。我和我的应用程序的主要问题是我的数据模型是这样的,我的播种和重复数据删除策略需要知道该设备是否是第一个与 iCloud 同步的设备。有些应用程序可能并不在乎,但我的却在乎。我仍在尝试创建一个可靠的工作,但到目前为止,没有运气。我即将用尽所有选项,并推迟向我现有的应用程序添加 iCloud 支持的请求,直到这些问题得到解决。我在 Apple iOS 开发者网站上问过同样的问题,完全没有任何回应或兴趣。