ios - 无法从在第一台设备上写入的第二台设备读取iCloud文件

标签 ios icloud

我无法在一个设备上从我的应用程序向iCloud容器中写入一个简单的文件,然后在我的应用程序第一次在另一台设备上运行时成功读取该文件

我从一对新重置的设备上的空Ubiquity容器开始了此测试方案。下面是一个测试用例代码和控制台日志,它演示了我遇到的问题。下面的所有代码都在后台线程上运行。

我在第一个设备上安装并运行测试用例。代码在普适性容器中查找某个文件,并在找不到文件时创建它。显然,这是首次在第一台设备上运行时所期望的。我希望(并期望)当该应用程序全新安装并在第二台设备上运行时,它将能够查找和读取在第一台设备上创建的文件的内容。似乎在重置设备上全新安装的应用程序永远无法找到和读取现有文件,并且始终认为它需要创建一个新文件,不幸的是,如果该文件已经存在,最终将替换无处不在容器中的文件。

但是,在第一次尝试(在第二台设备上)找不到并读取文件后,如果我删除了该应用程序,然后重新安装并在同一设备上重新运行它,它确实会找到该文件并且能够读取其当前内容。似乎该应用程序的首次运行会​​导致该文件被加载到普遍存在的缓存中,但未处于该应用程序可以找到的状态。该应用程序的第二次运行似乎在缓存中找到了文件。

我知道您应该在iOS设备上开始下载文件,但是对于设备而言,它第一次报告“找不到文件”。在这里还有其他需要做的事情吗?还是看起来像是新的或已知的错误?

以下代码用于确定 token 文件在普适容器中的存在并确保可以读取它。

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);

以下代码用于尝试从普遍存在的容器中读取 token 文件。
__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);
 }];

如果无法读取现有 token 文件,则以下代码用于将 token 文件写入到普遍存在的容器中。我期望该代码只能在第一个设备上运行一次。
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 Container中。然后,该应用程序将在名为“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)

对于此测试,该应用在尝试读取后停止,并且不允许尝试写入或替换 token 文件。然后,该应用将被杀死,删除并重新安装,然后在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

这次它将查找并读取具有预期内容的文件。

最佳答案

好吧,我没有放弃,现在我认为我可以解决这个问题。
解决方案的关键是第二台设备能够成功检测到第一台设备已将其写入到遍在容器中的 token 文件,而不必尝试自己编写文件。

以下是对我有用的步骤:

  • 立即尝试从容器中读取 token 文件。
  • 如果成功,请使用该文件,然后忽略其余步骤。
  • 否则,启动一个元数据查询,该查询的谓词将在两个特定文件名(您尝试读取的 token 文件和一个“虚拟”文件)上触发。
  • 现在,使用虚拟文件名将文件写入到普遍存在的容器中。
  • 当元数据查询的结果到达时,检查其报告的文件的上传/下载状态。
  • 如果查询曾经报告看到 token 文件,则这不是第一个设备,它应继续尝试读取文件直到成功。可能要花一些时间,但最终还是会阅读。
  • 如果元数据查询报告虚拟文件已完全上传,并且仍然没有 token 文件的迹象,那么可以安全地假定这是第一个设备,现在可以编写 token 文件了。
  • 我发现在尝试使用 token 之前等待 token 文件的状态完全上传是很有用的。否则,如果存在网络问题,另一个也开始此过程的设备可能会做同样的事情,并认为它是第一台设备。

  • 似乎将伪文件写入到普遍存在的容器会“启动”元数据查询,足以使它报告第二个设备上 token 文件的存在。没有它,元数据查询将永远不会报告 token 文件在第一个设备以外的任何设备上的存在。

    关于ios - 无法从在第一台设备上写入的第二台设备读取iCloud文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13503194/

    相关文章:

    ios - UIAppearance 色调颜色优先于 Storyboard 色调颜色

    ios - CloudKit,无法修改某些记录 - 未找到区域

    ios - 为什么 PHAssetCollection 计数为 0?

    ios - 如何从 firebase 数据库中以字符串而不是字典的形式检索数据并将其添加到数组中?

    ios - CloudKit - 通过 URL 在用户之间共享文件

    ios - 跨设备同步核心数据

    macos - iCloud 文档存储中的通知

    ios - iCloud 和核心数据错误(无处不在 : Didn't get baseline metadata back from metadata url)

    ios - 将 [String] 存储在 NSUserDefaults 中

    ios - Swift中如何保持Dictionary中数据的顺序?