【问题标题】:How to use iCloud to store and sync app files如何使用 iCloud 存储和同步应用程序文件
【发布时间】:2011-12-12 15:45:07
【问题描述】:

我已经有一个将数据存储在本地文档文件夹中的文件中的 iPhone 应用程序。现在我了解了 iCloud 技术,我的第一个问题是:有没有办法在有时检查新版本时使用 iCloud 作为目录?

我的意思是:我可以避免使用 UIDocument、文件协调器和文件展示器吗?我只想知道是否可以将 iCloud 视为一个特殊的文件夹,并且只使用 NSFileManager 来推送和检索文件。

最后一点:我不使用 Core Data 或任何数据库,我只有一个数据文件。

编辑:

我已经阅读了 Apple iCloud 官方文档,所以不要将我链接到它们。我只需要一些代码示例。

【问题讨论】:

    标签: objective-c cocoa-touch nsfilemanager icloud


    【解决方案1】:

    对我来说“有效”的方法很简单:

    NSFileManager *fm = [NSFileManager defaultManager];
    
    NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    
    if (ubiq == nil) {
        return NO;
    }
    
    NSError *theError = nil;
    
    [fm setUbiquitous:true itemAtURL:backupUrl destinationURL:[[ubiq URLByAppendingPathComponent:@"Documents" isDirectory:true] URLByAppendingPathComponent:backupName] error:&theError];
    

    Apple 说要调用非 UI 线程。让文件“移动”。您可以像这样通过NSMetaDataQuery查询它们:

    self.query = [[NSMetadataQuery alloc] init];
    [self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
    NSPredicate *pred = [NSPredicate predicateWithFormat: @"%K like '*.db'", NSMetadataItemFSNameKey];
    [self.query setPredicate:pred];
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(queryDidFinishGathering:) 
                                                 name:NSMetadataQueryDidFinishGatheringNotification 
                                               object:self.query];
    
    [self.query startQuery];
    
    - (void)queryDidFinishGathering:(NSNotification *)notification {
        NSMetadataQuery *query = [notification object];
        [query disableUpdates];
        [query stopQuery];
    
        [self loadData:query];
    
        [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:query];
    
        self.query = nil; 
    }
    

    通过查询结果枚举示例:

    - (void)loadData:(NSMetadataQuery *)query {
        [self.backups removeAllObjects];
    
        for (NSMetadataItem *item in [query results]) {
            NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
            [self.backups addObject:url.lastPathComponent];
        }
    
        [_table reloadData];
    
        [self.loadingBackupIndicator stopAnimating];
        self.loadingIndicatorLabel.text = [NSString stringWithFormat: @"%d backups found", [self.backups count]];
    }
    

    并开始“下载”具体文件:

    NSFileManager *fm = [NSFileManager defaultManager];
    
    NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    
    if (ubiq == nil) {
        return NO;
    }
    
    NSError *theError = nil;
    
    bool started = [fm startDownloadingUbiquitousItemAtURL:[[ubiq URLByAppendingPathComponent:@"Documents" isDirectory:true] URLByAppendingPathComponent:backupName] error:&theError];
    
    NSLog(@"started download for %@ %d", backupName, started);
    
    if (theError != nil) {
        NSLog(@"iCloud error: %@", [theError localizedDescription]);
    }
    

    检查文件“正在下载”:

    - (BOOL)downloadFileIfNotAvailable {
        NSNumber *isIniCloud = nil;
    
        NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    
        NSURL *file = [[ubiq URLByAppendingPathComponent:@"Documents" isDirectory:true] URLByAppendingPathComponent:self.backupName];
    
        if ([file getResourceValue:&isIniCloud forKey:NSURLIsUbiquitousItemKey error:nil]) {
            // If the item is in iCloud, see if it is downloaded.
            if ([isIniCloud boolValue]) {
                NSNumber*  isDownloaded = nil;
                if ([file getResourceValue:&isDownloaded forKey:NSURLUbiquitousItemIsDownloadedKey error:nil]) {
                    if ([isDownloaded boolValue]) {
                        [self.loadingBackupIndicator stopAnimating];
                        self.loadingIndicatorLabel.text = @"Downloaded";
    
                        ....
    
                        [[NSFileManager defaultManager] copyItemAtPath:[file path] toPath:restorePath error:&theError ];
    
                        ....
    
                        return YES;
                    }
    
                    self.loadingCheckTimer = [NSTimer timerWithTimeInterval:3.0f target:self selector:@selector(downloadFileIfNotAvailable) userInfo:nil repeats:NO];
                    [[NSRunLoop currentRunLoop] addTimer:self.loadingCheckTimer forMode:NSDefaultRunLoopMode];
    
                    return NO;
                }
            }
        }
    
        return YES;
    }
    

    我没想到代码会那么长,很抱歉在这里提供了非常原始的 sn-ps。无意说以上可以是生产质量的代码,只是分享概念。

    我还没有在我的应用程序中将它提交给 Apple,所以不能说它会被应用商店“批准”(如果他们发现或关心......)

    【讨论】:

    • 只是要注意上面代码的“生产”版本将其在应用程序内部传递到应用程序商店。
    • 能否请您提供一个包含上述代码的示例项目。如果可能,请帮助我。
    • @StanislavDvoychenko 你好,先生,请给我一些关于 icloud 编码部分的想法。意思是如何存储和检索等。请提前谢谢
    • NSURLUbiquitousItemIsDownloadedKey 在 iOS7 中已被弃用,取而代之的是 NSURLUbiquitousItemDownloadingStatusKey。我还要感谢 Stanislav 发布了如此完整且有用的示例。
    • 我们可以通过 iCloud 共享链接吗?就像在 Pages 应用程序和 Key not 应用程序中一样。如何设置文件权限和所有权限并通过电子邮件共享链接
    【解决方案2】:

    我知道你的感受,iCloud 有点令人生畏。但是,我认为 UIDocument、文件协调器等没有办法简单地将 iCloud 用作一个简单的文件夹。

    如果您正在寻找易于理解的示例代码,请查看此帖子:

    iCloud basics and code sample

    我包含了一个完整的示例代码,它涵盖了 iCloud 的最低限度,并且几乎像目录一样使用它。也许这让您使用 UIDocument、文件协调器等变得不那么令人生畏。

    但是,像你一样,我希望有一种更简单、更兼容的方式与旧的纪录片文件​​夹的想法。然而,由于这是 iCloud,而且 iCloud 做了更多的事情(比如在不同的设备上保持同步,不断更新到云等),所以没有办法绕过 UIDocument 等。

    【讨论】:

    • 我同意,如果你说没有办法,我会看教程。但我认为应该有一种更简单的方法来做到这一点,比如在应用程序沙箱中写入或读取文件时。谢谢
    【解决方案3】:

    您可以使用 NSFileManager 将单个文件上传到 iCloud。我在我的博客上发布了一个complete walkthrough,关于如何做到这一点,但这里是相关的 NSFileManager 代码:

    NSURL *destinationURL = [self.ubiquitousURL URLByAppendingPathComponent:@"Documents/image.jpg"]
    [[NSFileManager defaultManager] setUbiquitous:YES 
                                        itemAtURL:sourceURL
                                   destinationURL:destinationURL
                                            error:&error]
    

    【讨论】:

    • 有没有办法复制而不是移动?
    • 当我这样做时,我尝试使用 residentData = [[NSMutableArray alloc] initWithContentsOfURL:icloudURL]; 获取 iCloud 中的文件。但这每次都返回 nil ..
    【解决方案4】:

    真的没有办法使用 UIDocument。我尝试在我第一次使用 iCloud 时做到这一点,但结果证明没有 UIDocument 是一场灾难。起初使用 UIDocument 似乎需要做很多额外的工作,但事实并非如此。

    您可以在一小时内轻松地继承 UIDocument 并让它与任何类型的文件一起使用(只需将 content 属性设置为 NSData)。与标准文件系统相比,它还提供了许多好处:

    • 更改跟踪
    • 文件冲突解决
    • 文档状态支持
    • 增强的保存/打开/关闭功能

    老实说,花一两个小时阅读 Apple 文档然后使用它非常值得花时间和脑力。可以在Apple's Developer Documentation 找到一篇关于 iCloud 文档存储的入门文章。


    我编写了一个 UIDocument 子类,它可以处理任何类型的文件(特别是 NSData)。您可以在GitHub 上查看、下载和修改 UIDocument 子类的代码。

    创建文档:

    // Initialize a document with a valid file path
    iCloudDocument *document = [[iCloudDocument alloc] initWithFileURL:fileURL];
    
    // Set the content of the document
    document.contents = content;
    
    // Increment the change count
    [document updateChangeCount:UIDocumentChangeDone];
    

    保存现有文档:

    // Save and close the document
    [document closeWithCompletionHandler:nil];
    

    保存一个新文档:

    [document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:nil];
    

    您还可以使用 NSMetadataQuery 同步存储在 iCloud 中的所有文件。 Apple 提供了一个非常好的使用 NSMetadata 查询来同步应用文件的示例。还要确保在执行这些操作之前检查 iCloud(提示:使用 NSFileManager 上的ubiquityIdentityToken 方法)。


    您可能还想考虑使用开源库,例如 iCloud Document Sync。 iCloud Document Sync 项目使存储和同步应用文件变得非常容易:

    使用一行代码方法将 iCloud 集成到 iOS 文档项目中。快速轻松地从 iCloud 同步、上传、管理和删除文档。有助于使 iCloud 也对开发人员“正常工作”。

    在几乎所有 iCloud 文档同步方法中,您所要做的就是将文件数据作为参数传递,然后由它处理其余部分(保存、同步等)。

    免责声明:我是开源项目 iCloud Document Sync 的一名贡献开发人员。但是,我相信这个项目会对你有益,并且与这个问题相关。这不是促销或广告。

    【讨论】:

    • 这正是我最后所做的。
    • 如果您的用例是将数据转储到 iCloud,您可以使用文件协调器来完成,不需要 UIDocument。
    【解决方案5】:

    使用此代码:

    + (NSString *)Pathsave {
        NSString *os5 = @"5.0";
    
        NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
        NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    
        if ([currSysVer compare:os5 options:NSNumericSearch] == NSOrderedAscending) {
           // Lower than 4
            return path;
        } else if ([currSysVer compare:os5 options:NSNumericSearch] == NSOrderedDescending) {
            // 5.0.1 and above        
            return path;
        } else {
            // iOS 5
            path = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"];
            return path;
        }
    
        return nil;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-01-24
      • 2011-12-24
      • 2014-12-26
      • 1970-01-01
      • 1970-01-01
      • 2012-04-07
      • 2017-01-16
      • 2013-07-09
      • 1970-01-01
      相关资源
      最近更新 更多