iOS 8中动态生成的文件“Open In”失败

时间:2014-10-09 21:14:21

标签: ios filesystems nsdata uidocumentinteraction

我使用此代码将一些PDF数据保存到文件中,使用“打开方式”菜单将其发送到另一个应用程序,然后在完成后删除该文件:

- (void)openIn:(NSData *)fileData {
    // save the PDF data to a temporary file
    NSString *fileName = [NSString stringWithFormat:@"%@.pdf", self.name];
    NSString *filePath = [NSString stringWithFormat:@"%@/Documents/%@", NSHomeDirectory(), fileName];
    BOOL result = [fileData writeToFile:filePath atomically:TRUE];
    if (result) {
        NSURL *URL = [NSURL fileURLWithPath:filePath];
        UIDocumentInteractionController *controller = [[UIDocumentInteractionController interactionControllerWithURL:URL] retain];
        controller.delegate = self;
        [controller presentOpenInMenuFromBarButtonItem:self.openInButton animated:TRUE];
    }
}

- (void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller {
    // when the document interaction controller finishes, delete the temporary file
    NSString *fileName = [NSString stringWithFormat:@"%@.pdf", self.name];
    NSString *filePath = [NSString stringWithFormat:@"%@/Documents/%@", NSHomeDirectory(), fileName];
    [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}

这在iOS 8之前一直运行正常。现在,文件已创建,我可以验证它包含正确的内容,出现打开菜单,我可以选择一个应用程序,并且委托方法运行并清理文件。但是,不是iOS切换到所选应用程序并将文件复制到之前,当我选择应用程序时,打开输入菜单就会关闭,而且文件不会被复制。

如果我给UIDocumentInteractionController一个现有文件,这是有效的。如果我使用提供的fileData但将目标文件名更改为现有文件的文件名,它也可以工作。这表明存在权限问题 - 就像在iOS 8中使用UIDocumentInteractionController无法读取的默认权限创建新文件一样。

有谁知道发生了什么以及如何解决这个问题?

2 个答案:

答案 0 :(得分:7)

看起来iOS 8中的操作顺序略有变化.DidDismissOpenInMenu曾经在文件发送完成后运行,但现在它在文件开始发送后运行。这意味着我的清理代码有时会在文件完成发送之前运行,不会发送任何文件。在注意到较小的文件被发送好后,我想出了这个;显然,在我的清理代码获得之前,对较小文件的处理已经完成,但是对较大文件的处理却没有。

为了确保正确的计时,还要清理用户打开DocumentInteractionController时创建的文件,然后在不做任何事情的情况下解除控制器,我改变了我的方法:

- (void)openIn:(NSData *)fileData {
    // save the PDF data to a temporary file
    NSString *fileName = [NSString stringWithFormat:@"%@.pdf", self.name];
    NSString *filePath = [NSString stringWithFormat:@"%@/Documents/%@", NSHomeDirectory(), fileName];
    BOOL result = [fileData writeToFile:filePath atomically:TRUE];
    if (result) {
        self.sendingFile = FALSE;
        NSURL *URL = [NSURL fileURLWithPath:filePath];
        UIDocumentInteractionController *controller = [[UIDocumentInteractionController interactionControllerWithURL:URL] retain];
        controller.delegate = self;
        [controller presentOpenInMenuFromBarButtonItem:self.openInButton animated:TRUE];
    }
}

- (void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application {
    // the user chose to send the file, so we shouldn't clean it up until that's done
    self.sendingFile = TRUE;
}

- (void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller {
    if (!self.sendingFile) {
        // the user didn't choose to send the file, so we can clean it up now
        [self openInCleanup];
    }
}

- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
    // the user chose to send the file, and the sending is finished, so we can clean it up now
    [self openInCleanup];
    self.sendingFile = FALSE;
}

- (void)openInCleanup {
    // delete the temporary file
    NSString *fileName = [NSString stringWithFormat:@"%@.pdf", self.name];
    NSString *filePath = [NSString stringWithFormat:@"%@/Documents/%@", NSHomeDirectory(), fileName];
    [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}

iOS 11更新

在iOS 11之前,操作系统似乎保留了该文件的副本,直到接收应用程序完成读取,即使我的清理功能在文件从我的应用程序发出后立即运行。在iOS 11中,此更改并且接收应用程序无法读取文件,因为我的应用程序在完成之前将其删除。所以现在不是将临时文件保存到Documents并使用openInCleanup方法立即删除它,而是将临时文件保存到tmp并在下次启动应用程序时清空tmp文件夹。此方法也适用于较旧的iOS版本。只需删除openInCleanup,在路径中将文档更改为tmp,然后将其添加到applicationDidFinishLaunching

// clear the tmp directory, which will contain any files saved for Open In
NSString *tmpDirectory = [NSString stringWithFormat:@"%@/tmp", NSHomeDirectory()];
NSArray *tmpFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpDirectory error:NULL];
for (NSString *tmpFile in tmpFiles) {
    [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@/%@", tmpDirectory, tmpFile] error:NULL];
}

答案 1 :(得分:1)

阅读这篇文章后,我已经希望找到解决类似问题的方法:
对我来说,从iOS 8开始,共享仅适用于Mail.app。 Dropbox等失败了。

原来这是别的东西:
在我的interactionController上,我设置了一个这样的注释:

interactionController.annotation = @"Some text"

由于未知原因,这会阻止Dropbox打开。没有错误消息或任何内容。删除此行解决了这个问题。