简体   繁体   English

如何使用 iCloud 存储和同步应用程序文件

[英]How to use iCloud to store and sync app files

I already have an iPhone App that stores data in a file in the local documents folder.我已经有一个 iPhone 应用程序,可以将数据存储在本地文档文件夹中的文件中。 Now I learnt about iCloud technologies and my first question was: is there a way to use iCloud as a directory when sometimes I check for new versions?现在我了解了 iCloud 技术,我的第一个问题是:当我有时检查新版本时,有没有办法将 iCloud 用作目录?

I mean: can I avoid using UIDocument, file coordinators and file presenters?我的意思是:我可以避免使用 UIDocument、文件协调器和文件展示器吗? I want just to know if could treat iCloud like a special folder and only use NSFileManager to push and retrieve files.我只想知道是否可以将 iCloud 视为一个特殊的文件夹,并且只使用 NSFileManager 来推送和检索文件。

Last note: I don't use Core Data or any database, I only have a data file.最后一点:我不使用 Core Data 或任何数据库,我只有一个数据文件。

Edit:编辑:

I already read the official Apple iCloud documentation so don't link me to them.我已经阅读了 Apple iCloud 官方文档,所以不要将我链接到它们。 I only need some code examples.我只需要一些代码示例。

What "works" for me is just simple:对我来说“有效”的方法很简单:

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 says to call on the non-UI thread. Apple 说要调用非 UI 线程。 Having the files "moved".让文件“移动”。 You can query for them via NSMetaDataQuery like this:您可以通过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; 
}

Sample of enumeration through the query results:通过查询结果进行枚举的示例:

- (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]];
}

And to start "download" of the concrete file:并开始“下载”具体文件:

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

With checks for file "being downloaded":检查文件“正在下载”:

- (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;
}

I didn't expect the code to be that long and sorry for providing very raw snippets here.我没想到代码这么长,很抱歉在这里提供了非常原始的片段。 No intent to say the above can be a production quality of code, just sharing the concept.无意说以上可以是代码的生产质量,只是分享概念。

I have not yet submitted that inside my app to Apple, so can't tell that would be "approved" to the app store (if they find or care...)我还没有在我的应用程序中将它提交给 Apple,所以不能说它会被“批准”到应用程序商店(如果他们发现或关心......)

I know how you feel, iCloud is a bit daunting.我知道你的感受,iCloud 有点令人生畏。 However, I think there is no way around UIDocument, file coordinators etc. and to simply use iCloud as a simple folder.但是,我认为没有办法绕过 UIDocument、文件协调器等,而只能将 iCloud 用作一个简单的文件夹。

If you are looking for an easy to understand sample code, please have a look at this post:如果您正在寻找易于理解的示例代码,请查看这篇文章:

iCloud basics and code sample iCloud 基础知识和代码示例

I included a full sample code which covers the bare minimums of iCloud and pretty much uses it like a directory.我包含了一个完整的示例代码,它涵盖了 iCloud 的最低要求,并且几乎像目录一样使用它。 Perhaps this makes it less daunting for you to use UIDocument, file coordinators etc.也许这会让你使用 UIDocument、文件协调器等不那么令人生畏。

But, like you, I wish there was an easier and more compatible way with the good old documentary folder idea.但是,像你一样,我希望有一种更简单、更兼容的方式来与旧的纪录片文件​​夹想法相匹配。 However, as this is iCloud and as iCloud does several things more (like keeping everything in sync on different devices, constantly updating to cloud etc.), there will be no way around UIDocument etc.但是,由于这是 iCloud,并且 iCloud 还做了几件事(例如在不同设备上保持所有内容同步,不断更新到云等),因此将无法绕过 UIDocument 等。

You can upload individual files to iCloud using NSFileManager.您可以使用 NSFileManager 将单个文件上传到 iCloud。 I posted a complete walkthrough on how to do that on my blog, but here's the relevant NSFileManager code:我在我的博客上发布了有关如何执行此操作完整演练,但这是相关的 NSFileManager 代码:

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

There isn't really a way around using UIDocument.没有真正的方法可以使用 UIDocument。 I tried to do it in one of my first uses of iCloud, but it turned out to be a disaster without UIDocument.我尝试在我第一次使用 iCloud 时做到这一点,但结果证明没有 UIDocument 是一场灾难。 Using UIDocument at first seems like a lot of extra work, but it isn't.一开始使用 UIDocument 似乎有很多额外的工作,但事实并非如此。

You can easily subclass UIDocument in under an hour and get it to work with any type of file (just set the content property as NSData).您可以在一个小时内轻松地将 UIDocument 子类化,并使其适用于任何类型的文件(只需将content属性设置为 NSData)。 It also provides numerous benefits over the standard file system:与标准文件系统相比,它还提供了许多好处:

  • Change tracking变更追踪
  • File conflict resolution文件冲突解决
  • Document state support文档状态支持
  • Enhanced save / open / close features增强的保存/打开/关闭功能

Honestly, spending just an hour or two reading over the Apple documentation and then using it is well worth the time and brain power.老实说,花一两个小时阅读 Apple 文档然后使用它是非常值得的。 A good starter article on iCloud Document storage can be found in Apple's Developer Documentation .可以在Apple 的开发人员文档 中找到有关 iCloud 文档存储的优秀入门文章。


I have written a UIDocument subclass that will work with any type of file (NSData specifically).我编写了一个 UIDocument 子类,它可以处理任何类型的文件(特别是 NSData)。 You can view, download, and modify the code for the UIDocument subclass on GitHub .您可以在GitHub 上查看、下载和修改 UIDocument 子类的代码。

Create the document:创建文档:

// 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 an existing document:保存现有文档:

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

Save a new document:保存新文档:

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

You can also sync all files stored within iCloud by using NSMetadataQuery.您还可以使用 NSMetadataQuery 同步存储在 iCloud 中的所有文件。 Apple provides a very nice example of using NSMetadata query to sync app files. Apple 提供了一个使用 NSMetadata 查询来同步应用程序文件的非常好的示例。 Also make sure to check for iCloud before performing these operations (hint: use the ubiquityIdentityToken method on NSFileManager).还要确保在执行这些操作之前检查 iCloud(提示:在 NSFileManager 上使用ubiquityIdentityToken方法)。


You may also want to consider using an open-source library such as iCloud Document Sync .您可能还需要考虑使用开源库,例如iCloud Document Sync The iCloud Document Sync project makes it very easy to store and sync app files: iCloud 文档同步项目使存储和同步应用程序文件变得非常容易:

Integrate iCloud into iOS document projects with one-line code methods.用一行代码的方式将 iCloud 集成到 iOS 文档项目中。 Sync, upload, manage, and remove documents from iCloud quickly and easily.快速轻松地从 iCloud 同步、上传、管理和删除文档。 Helps to make iCloud "just work" for developers too.也有助于使 iCloud 对开发人员“正常工作”。

In almost every iCloud Document Sync method, all you have to do is pass in your file data as a parameter and then it handles the rest (saving, syncing, etc.).在几乎所有 iCloud 文档同步方法中,您所要做的就是将文件数据作为参数传递,然后它会处理其余的(保存、同步等)。

DISCLAIMER : I am a contributing developer to the open-source project, iCloud Document Sync.免责声明:我是开源项目 iCloud 文档同步的贡献开发人员。 However, I believe that this project will be beneficial to you, and is relevant to this question.但是,我相信这个项目会对您有所帮助,并且与这个问题相关。 This is not a promotion or advertisement.这不是促销或广告。

Use this code:使用此代码:

+ (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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM