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

发布于 2024-12-11 03:20:48 字数 336 浏览 3 评论 0原文

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

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

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

编辑:

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

I already have an iPhone App that stores data in a file in the local documents folder. 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?

I mean: can I avoid using UIDocument, file coordinators and file presenters? I want just to know if could treat iCloud like a special folder and only use NSFileManager to push and retrieve files.

Last note: I don't use Core Data or any database, I only have a data file.

Edit:

I already read the official Apple iCloud documentation so don't link me to them. I only need some code examples.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

情未る 2024-12-18 03:20:48

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

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

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

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

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. Having the files "moved". You can query for them via NSMetaDataQuerylike this:

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

酒几许 2024-12-18 03:20:48

我知道你的感受,iCloud 有点令人畏惧。然而,我认为没有办法绕过 UIDocument、文件协调器等并简单地使用 iCloud 作为一个简单的文件夹。

如果您正在寻找易于理解的示例代码,请查看这篇文章:

iCloud 基础知识和代码示例

我提供了完整的示例代码,其中涵盖了 iCloud 的最低限度,并且几乎像目录一样使用它。也许这会让您在使用 UIDocument、文件协调器等时不再那么畏惧。

但是,像您一样,我希望有一种更简单、更兼容的方式来实现良好的旧文档文件夹想法。然而,由于这是 iCloud,而且 iCloud 还可以做更多事情(例如在不同设备上保持所有内容同步、不断更新到云等),因此无法绕过 UIDocument 等。

I know how you feel, iCloud is a bit daunting. However, I think there is no way around UIDocument, file coordinators etc. and to simply use iCloud as a simple folder.

If you are looking for an easy to understand sample code, please have a look at this post:

iCloud basics and code sample

I included a full sample code which covers the bare minimums of iCloud and pretty much uses it like a directory. Perhaps this makes it less daunting for you to use UIDocument, file coordinators etc.

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.

偏闹i 2024-12-18 03:20:48

您可以使用 NSFileManager 将单个文件上传到 iCloud。我在我的博客上发布了关于如何执行此操作的完整演练,但以下是相关的 NSFileManager 代码:

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

You can upload individual files to iCloud using NSFileManager. I posted a complete walkthrough on how to do that on my blog, but here's the relevant NSFileManager code:

NSURL *destinationURL = [self.ubiquitousURL URLByAppendingPathComponent:@"Documents/image.jpg"]
[[NSFileManager defaultManager] setUbiquitous:YES 
                                    itemAtURL:sourceURL
                               destinationURL:destinationURL
                                        error:&error]
中性美 2024-12-18 03:20:48

确实没有办法绕过使用 UIDocument。我在第一次使用 iCloud 时尝试这样做,但事实证明,如果没有 UIDocument,这将是一场灾难。使用 UIDocument 乍一看似乎需要做很多额外的工作,但事实并非如此。

您可以在一小时内轻松地对 UIDocument 进行子类化,并使其能够处理任何类型的文件(只需将 content 属性设置为 NSData)。与标准文件系统相比,它还提供了许多好处:

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

老实说,花一两个小时阅读 Apple 文档然后使用它是非常值得花时间和精力的力量。有关 iCloud 文档存储的优秀入门文章可以在 Apple 的开发者文档


我编写了一个 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 Document Sync 方法中,您所要做的就是将文件数据作为参数传递,然后它会处理其余的事情(保存、同步等)。

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

There isn't really a way around using UIDocument. I tried to do it in one of my first uses of iCloud, but it turned out to be a disaster without UIDocument. Using UIDocument at first seems like a lot of extra work, but it isn't.

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). 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. A good starter article on iCloud Document storage can be found in Apple's Developer Documentation.


I have written a UIDocument subclass that will work with any type of file (NSData specifically). You can view, download, and modify the code for the UIDocument subclass on GitHub.

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. Apple provides a very nice example of using NSMetadata query to sync app files. Also make sure to check for iCloud before performing these operations (hint: use the ubiquityIdentityToken method on NSFileManager).


You may also want to consider using an open-source library such as iCloud Document Sync. The iCloud Document Sync project makes it very easy to store and sync app files:

Integrate iCloud into iOS document projects with one-line code methods. Sync, upload, manage, and remove documents from iCloud quickly and easily. Helps to make iCloud "just work" for developers too.

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

DISCLAIMER: I am a contributing developer to the open-source project, iCloud Document Sync. However, I believe that this project will be beneficial to you, and is relevant to this question. This is not a promotion or advertisement.

紫瑟鸿黎 2024-12-18 03:20:48

使用此代码:

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

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文