NSMetadataQuery无法找到iCloud文件

6
我按照苹果文档中描述的方法使用了NSMetadataQuery来搜索我的iCloud文件。我只有一个文件,而且我知道它的名字。我的问题是,有时这个文件不存在(我猜测是因为它还没有被下载),而NSMetadataQuery无法找到它。
我曾经尝试使用NSFileManager startDownloadingUbiquitousItemAtURL:error:来强制下载,但返回了一个错误。(请参见EDIT)
我的解决方案是,第一次创建文件,然后我猜它存在并用UIDocument打开。但它可能不存在,或者可能是用户第一次打开应用程序。我无法确定这些事情。我的第一个问题是:如果UIDocument打开文件,那么它意味着在某处找到了文件。如果文件不存在,它将如何使用该文件?
其次,第二个问题是:如果我需要管理多个文件或未知名称的文件的应用程序,如果NSMetadataQuery不起作用,我该如何找到它们。
编辑:如果应该使用startDownloadingUbiquitousItemAtURL来开始下载文件,我如何知道文件何时完成下载(也许可以通过通知)?但更重要的是:如果始终说“删除原始名称”,我该如何下载文件?
   Error Domain=NSPOSIXErrorDomain Code=2 "The operation couldn’t be completed.
    No such file or directory" UserInfo=0x166cb0 {
    NSDescription=Unable to get real path for Path
'/private/var/mobile/Library/Mobile Documents/teamid~com~team~app/Documents/file.extension'
    }
3个回答

7
我建议以下iCloud文件加载程序。它涉及4个步骤:
  1. 首先测试iCloud是否可访问
  2. 然后查找您的文件(可以查找特定文件,如您所指示的那样,也可以查找某个扩展名的所有文件,例如*.txt,或者如果您不知道要查找什么文件扩展名,则像NSPredicate * pred = [NSPredicate predicateWithFormat @“NOT%K.pathExtension ='.'”,NSMetadataItemFSNameKey]; 这样的语句将返回所有具有扩展名的文件,例如jpg、txt、dat等)
  3. 接着检查查询是否完成,
  4. 最后尝试加载文件。如果文件不存在,则创建它。如果存在,则加载它。
以下是示例代码,演示了这四个步骤:
    - (void)loadData:(NSMetadataQuery *)query {

    // (4) iCloud: the heart of the load mechanism: if texts was found, open it and put it into _document; if not create it an then put it into _document

    if ([query resultCount] == 1) {
        // found the file in iCloud
        NSMetadataItem *item = [query resultAtIndex:0];
        NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];

        MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:url];
        //_document = doc;
        doc.delegate = self.viewController;
        self.viewController.document = doc;

        [doc openWithCompletionHandler:^(BOOL success) {
            if (success) {
                NSLog(@"AppDelegate: existing document opened from iCloud");
            } else {
                NSLog(@"AppDelegate: existing document failed to open from iCloud");
            }
        }];
    } else {
        // Nothing in iCloud: create a container for file and give it URL
        NSLog(@"AppDelegate: ocument not found in iCloud.");

        NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
        NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:@"Documents"] URLByAppendingPathComponent:@"text.txt"];

        MyTextDocument *doc = [[MyTextDocument alloc] initWithFileURL:ubiquitousPackage];
        //_document = doc;
        doc.delegate = self.viewController;
        self.viewController.document = doc;

        [doc saveToURL:[doc fileURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
            NSLog(@"AppDelegate: new document save to iCloud");
            [doc openWithCompletionHandler:^(BOOL success) {
                NSLog(@"AppDelegate: new document opened from iCloud");
            }];
        }];
    }
}

- (void)queryDidFinishGathering:(NSNotification *)notification {

    // (3) if Query is finished, this will send the result (i.e. either it found our text.dat or it didn't) to the next function

    NSMetadataQuery *query = [notification object];
    [query disableUpdates];
    [query stopQuery];

    [self loadData:query];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query];
    _query = nil; // we're done with it
}

-(void)loadDocument {

    // (2) iCloud query: Looks if there exists a file called text.txt in the cloud

    NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
    _query = query;
    //SCOPE
    [query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
    //PREDICATE
    NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K == %@", NSMetadataItemFSNameKey, @"text.txt"];
    [query setPredicate:pred];
    //FINISHED?
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];
    [query startQuery];

}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSLog(@"AppDelegate: app did finish launching");
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];

    // Override point for customization after application launch.
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil] autorelease];
    } else {
        self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil] autorelease];
    }

    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];

    // (1) iCloud: init

    NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    if (ubiq) {
        NSLog(@"AppDelegate: iCloud access!");
        [self loadDocument];
    } else {
        NSLog(@"AppDelegate: No iCloud access (either you are using simulator or, if you are on your phone, you should check settings");
    }


    return YES;
}

我已经在做这件事了。当我开始使用iCloud时,我做的第一件事就是阅读你所有的帖子。 - albianto
你执行一个查询(不会下载任何东西),并且必须等待查询完成。这就是为什么要使用[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryDidFinishGathering:) name:NSMetadataQueryDidFinishGatheringNotification object:query];的原因; [query startQuery]; - n.evermind
由于查询有时会返回空结果,但文件刚刚被其他设备创建。 - albianto
1
找到问题了!!我在代码的另一部分中使用了文件协调器,我猜这可能会对UIDocument文件协调器造成一些麻烦。 - albianto
1
在开发和测试我的应用程序iCloud设置屏幕时,我遇到了同样的错误,一遍又一遍地删除设备和我的Mac中的文件,不知怎么搞得iCloud中的文件存储被损坏了,所以我不得不循环遍历普及文件夹中的文件,并从两个设备中手动删除所有内容,然后删除应用程序,删除任何可能显示在设置/icloud/manage docs/Myapp中的内容,然后重新安装开发应用程序在两个设备上,现在一切都正常了。祝其他遇到这个问题的人好运。 - J3RM
显示剩余5条评论

1

edo42,你解决了这个问题吗?我买了一个新的4S回到AT&T,我已经备份了我的第一个4S,然后从备份中恢复到新的4S,但是对我来说仍然出现了这个问题。

  1. 当我执行[iCloudFileURL getResource:&fileState forKey:NSURLIsUbiquitousItemKey error:&error]时,我得到了相同的错误。

Error Domain=NSPOSIXErrorDomain Code=2 "The operation couldn’t be completed. No such file or directory" UserInfo=0x448500 {NSDescription=Unable to get real path for Path '/private/var/mobile/Library/Mobile Documents/~UbiquitousDir~/Documents/MyDocument.ext'

(在我的iPad 2上工作正常)

  1. 然后我会执行NSMetadataQuery,但是它返回没有结果。(在我的iPad 2上可以正常工作,可以看到iCloud上的文档)。

  2. 我检查了普及文件是否存在,但是它并不存在,就像错误所示。

  3. 我尝试使用[fileMgr evictUbiquitousItemAtURL:ubiquitousDocumentsDir &error],它确实成功了。但是当我尝试NSMetadataQuery时,它仍然返回没有结果。

希望这有助于找出两种情况下的问题所在。

更新:好吧,对于这个从备份中恢复的设备,iCloud确实存在一个真正的问题:

  1. 我已经从iPhone设备中删除了我的应用程序。
  2. 我从“管理存储”中删除了我的应用程序的备份文件。
  3. 我甚至从Managed storage下的iCloud(文档和数据)中删除了我的应用程序文件。
  4. iCloud文件不再存在,我在iPad 2上查看它也消失了。
  5. 我在iPad 2上启动了我的应用程序,并成功保存了一个新的iCloud文档。
  6. 新的iCloud文件显示在我的iPhone上。
  7. 我在设备上构建和运行我的应用程序,但无法使用NSMetadaQuery查询下载文件。

备份恢复后,我注意到一个问题,我的应用程序不是几天前在设备上运行的已编译版本。因此,我编译了最新版本,现在出现了这个问题。 - Kenneth Lewis
在清除了无处不在的“文档”目录之后,我删除了“文档”目录和无处不在的目录,但仍然无法在iCloud上下载文件。 - Kenneth Lewis
同时,该文件也会显示在“设置/存储空间与备份/管理存储空间/文档和数据”中的iCloud下。 - Kenneth Lewis

0

我曾经遇到过类似的问题,metadataQueryDidUpdate和metadataQueryDidFinishGathering会触发,但NSMetadataQuery的结果为空。原来是Xcode没有告诉我一个供应配置文件的问题,直到我尝试在设备上测试它时才失败,并说我的包标识符无效(实际上不是)。

对我有用的解决方法是进入“偏好设置”,点击供应配置文件的“全部下载”按钮,然后在Xcode中进行清理。重新构建、运行,所有的文件都显示出来了。在开发者门户中检查,确保您的配置文件没有被标记为无效。我的没有被标记为无效,所以这不是必须的,但它可能会发生。

虽然不是特别科学,但这对我确实有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接