основы iCloud и пример кода [закрыт]
как новичок, я борюсь с iCloud. Есть несколько примеров, но они обычно довольно подробные (на форуме разработчиков есть один для iCloud и CoreData, который является массивным). Элемент apple docs в порядке, но я все еще не вижу общей картины. Поэтому, пожалуйста, потерпите меня, некоторые из этих вопросов довольно фундаментальны, но, возможно, легко ответить.
контекст: у меня есть очень простое приложение iCloud (полный пример кода ниже). Есть только один UITextView отображается пользователю, и его / ее ввод сохраняется в файле с именем text.формат txt.

файл txt помещается в облако и становится доступным для всех устройств. Работает отлично, но:
основная проблема: как насчет пользователей, которые не используют iCloud?
когда я запускаю свое приложение (см. код ниже), я проверяю, включен ли у пользователя iCloud. Если iCloud включен, все в порядке. Приложение идет вперед и ищет текст.txt in облако. Если он будет найден, он загрузит его и отобразит его пользователю. Если текст.txt не найден в облаке, он просто создаст новый текст.txt и покажет это пользователю.
если у пользователя не включен iCloud, ничего не произойдет. Как я могу сделать так, чтобы пользователи, не являющиеся пользователями iCloud, все еще могли работать с моим текстовым приложением? Или я просто игнорирую их? Нужно ли мне писать отдельные функции для пользователей, отличных от iCloud? Т. е. функции, в которые я просто загружаю текст.txt из документов папка?
обрабатывайте файлы в iCloud так же, как вы обрабатываете все другие файлы в песочнице вашего приложения.
однако в моем случае больше нет "нормальной" песочницы приложений. Это в облаке. Или я всегда загружаю свой текст.сначала txt с диска, а затем проверить с iCloud, если есть что-то более современное?
связанная проблема: структура файла-песочница против Облако
возможно, моя главная проблема заключается в фундаментальном непонимании того, как iCloud должен работать. Когда я создаю новый экземпляр UIDocument, мне придется перезаписать два метода. Первый - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError чтобы получить файлы из облака, а потом -(id)contentsForType:(NSString *)typeName error:(NSError **)outError чтобы получить файлы в облаке.
должен ли я включать отдельные функции, которые также сохранят локальную копию текста.txt в мою песочницу? Будет ли это работать для пользователей, отличных от iCloud? Как я понимаю iCloud, он сохранит локальная копия текста.тхт автоматически. Поэтому мне не нужно ничего сохранять в "старой" песочнице моего приложения (т. е. так, как это было в старые, предшествующие iCloud дни). Сейчас моя песочница совершенно пуста, но я не знаю, правильно ли это. Я должен сохранить еще одну копию текста.txt там? Это похоже на загромождение моей структуры данных... так же есть один текст.txt в облаке, один в песочнице iCloud на устройстве (который будет работать даже если я оффлайн), а третий в хорошем старая песочница моего приложения...
мой код: простой пример кода iCloud
это свободно основано на примере, который я нашел на форуме разработчиков и на видео сеанса WWDC. Я раздел его до минимума. Я не уверен, что моя структура MVC хороша. Модель находится в AppDelegate, который не является идеальным. Любые предложения, чтобы сделать его лучше приветствуются.
EDIT: я попытался извлечь основной вопрос и разместил его [здесь.]4
описание:

самый важный бит, который загружает текст.текст из облака:
// AppDelegate.h
// iCloudText
#import <UIKit/UIKit.h>
@class ViewController;
@class MyTextDocument;
@interface AppDelegate : UIResponder <UIApplicationDelegate> {
NSMetadataQuery *_query;
}
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) ViewController *viewController;
@property (strong, nonatomic) MyTextDocument *document;
@end
// AppDelegate.m
// iCloudText
#import "AppDelegate.h"
#import "MyTextDocument.h"
#import "ViewController.h"
@implementation AppDelegate
@synthesize window = _window;
@synthesize viewController = _viewController;
@synthesize document = _document;
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
- (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;
}
@end
UIDocument
// MyTextDocument.h
// iCloudText
#import <Foundation/Foundation.h>
#import "ViewController.h"
@interface MyTextDocument : UIDocument {
NSString *documentText;
id delegate;
}
@property (nonatomic, retain) NSString *documentText;
@property (nonatomic, assign) id delegate;
@end
// MyTextDocument.m
// iCloudText
#import "MyTextDocument.h"
#import "ViewController.h"
@implementation MyTextDocument
@synthesize documentText = _text;
@synthesize delegate = _delegate;
// ** READING **
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
{
NSLog(@"UIDocument: loadFromContents: state = %d, typeName=%@", self.documentState, typeName);
if ([contents length] > 0) {
self.documentText = [[NSString alloc] initWithBytes:[contents bytes] length:[contents length] encoding:NSUTF8StringEncoding];
}
else {
self.documentText = @"";
}
NSLog(@"UIDocument: Loaded the following text from the cloud: %@", self.documentText);
// update textView in delegate...
if ([_delegate respondsToSelector:@selector(noteDocumentContentsUpdated:)]) {
[_delegate noteDocumentContentsUpdated:self];
}
return YES;
}
// ** WRITING **
-(id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
if ([self.documentText length] == 0) {
self.documentText = @"New Note";
}
NSLog(@"UIDocument: Will save the following text in the cloud: %@", self.documentText);
return [NSData dataWithBytes:[self.documentText UTF8String] length:[self.documentText length]];
}
@end
VIEWCONTROLLER
//
// ViewController.h
// iCloudText
#import <UIKit/UIKit.h>
@class MyTextDocument;
@interface ViewController : UIViewController <UITextViewDelegate> {
IBOutlet UITextView *textView;
}
@property (nonatomic, retain) UITextView *textView;
@property (strong, nonatomic) MyTextDocument *document;
-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument;
@end
// ViewController.m
// iCloudText
#import "ViewController.h"
#import "MyTextDocument.h"
@implementation ViewController
@synthesize textView = _textView;
@synthesize document = _document;
-(IBAction)dismissKeyboard:(id)sender {
[_textView resignFirstResponder];
}
-(void)noteDocumentContentsUpdated:(MyTextDocument *)noteDocument
{
NSLog(@"VC: noteDocumentsUpdated");
_textView.text = noteDocument.documentText;
}
-(void)textViewDidChange:(UITextView *)theTextView {
NSLog(@"VC: textViewDidChange");
_document.documentText = theTextView.text;
[_document updateChangeCount:UIDocumentChangeDone];
}
5 ответов:
Я просто перечитал документы, и кажется, что мой общий подход неверен. Я должен сначала создать файл в песочнице, а затем переместить его в облако. Другими словами, Apple, похоже, предлагает мне всегда иметь три версии одного и того же файла: один в каталоге моего приложения, один в каталоге демона iCloud моего устройства (который также доступен в автономном режиме) и один в облаке:
приложения используют те же технологии для управления файлы и каталоги в iCloud, которые они делают для локальных файлов и каталогов. Файлы и каталоги в iCloud по-прежнему являются просто файлами и каталогами. Вы можете откройте их, создайте их, переместите их, скопируйте их, прочитайте и напишите из их, удалить их или любые другие операции, которые вы можете захотеть делать. Единственные различия между локальными файлами и каталогами и файлы и каталоги iCloud-это URL-адрес, который вы используете для доступа к ним. Вместо того, чтобы URL-адреса были относительно песочницы вашего приложения, URL-адреса для iCloud файлы и каталоги относятся к соответствующему iCloud каталог контейнеров.
чтобы переместить файл или каталог в iCloud:
создайте файл или каталог локально в песочнице приложения. а в используйте, файл или каталог должен управляться презентатором файлов, например как объект UIDocument.
использовать URLForUbiquityContainerIdentifier: способ, чтобы получить URL-адрес для iCloud каталог контейнеров, в котором вы хотите хранить пункт. Используйте URL-адрес каталога контейнера для создания нового URL-адреса, который задает расположение элемента в iCloud. Звоните setUbiquitous:itemAtURL:destinationURL:ошибка: метод NSFileManager чтобы переместить элемент в iCloud. Никогда не вызывайте этот метод из вашего приложения основной поток; это может заблокировать ваш основной поток для расширенного период времени или вызвать взаимоблокировку с одним из собственных файлов вашего приложения предъявители. При перемещении файла или каталога в iCloud, система копирует этот элемент из изолированной среды приложения в частный локальный компьютер каталог, чтобы он мог контролироваться демоном iCloud. Даже хотя файл больше не находится в песочнице, ваше приложение по-прежнему имеет полный доступ к нему. Хотя копия файла остается локальной для текущего устройство, файл также отправляется в iCloud, чтобы его можно было распространять на другие устройства. Демон iCloud обрабатывает всю работу по созданию убедитесь, что локальные копии одинаковы. Так что из перспектива ваше приложение, файл просто в iCloud.
все изменения, внесенные в файл или каталог в iCloud должны быть сделаны использование объекта файлового координатора. Эти изменения включают в себя перемещение, удаление, копирование или переименование элемента. Файловый координатор обеспечивает что демон iCloud не изменяет файл или каталог на в то же время и гарантирует, что другие заинтересованные стороны будут уведомлены изменения, которые вы делаете.
однако, если вы копаете немного глубже в документы, касающиеся setUbiquitous, вы найдете:
используйте этот метод для перемещения файла из его текущего местоположения в iCloud. Для файлов, расположенных в песочнице приложения, Это включает в себя физическое удаление файла из каталога песочницы. (Система расширяет права песочницы вашего приложения, чтобы предоставить ему доступ к файлам, которые он перемещает в iCloud.) Вы также можете использовать этот метод для перемещения файлов из iCloud и обратно в локальный справочник.
таким образом, это означает, что файл / каталог удаляется из локальной песочницы и перемещается в облако.
Я использую ваш пример, и мне нравится, что он помогает мне понять основы iCloud. Теперь я спорю с вашим вопросом для моего собственного приложения, которое должно поддерживать существующих пользователей приложения с локально сохраненным контентом, которые могут или не могут использовать iCloud, создавая эти случаи, насколько я могу судить:
случаях:
- новый пользователь
- имеет icloud-создание документов в icloud
- нет icloud-создание документов локально
- существующих пользователей
- имеет icloud
- только что добавлено-миграция локальных документов в icloud
- не просто добавил-открыть / сохранить документы в icloud
- нет icloud
- только что удалено-перенесите прежние документы icloud в локальный
- не просто удалены-открыть / сохранить документы в local
Если кто-то удаляет iCloud-не будут ли звонки вездесущими URL-адрес возвращает nil? Если это так, как мне перенести документы обратно в локальное хранилище? Я создам пользовательский преф на данный момент, но кажется немного обходным путем.
Я чувствую, что я упускаю что-то очевидное здесь, так что если кто-то может это увидеть, пожалуйста, перезвоните.
Если вы хотите, чтобы пользователи могли обмениваться текстом между устройствами, которые находятся до iOS 5.0, вам придется сделать то, что все должны были сделать до iCloud, и переместить информацию на свой собственный сервер.
все, что вам действительно нужно, это сервер где-то, что позволяет вашему приложению сохранять свои текстовые файлы и связывать их с учетной записью пользователя.
вам понадобятся пользователи для создания учетной записи, и вам нужно будет самостоятельно управлять процессом перемещения новой информации на одном устройстве в свой собственный "облако".
пользователи будут регистрироваться с той же учетной записью на других устройствах, и вам нужно будет позаботиться об обнаружении, когда другое устройство переместило данные в ваше собственное облако, и обновить текущее устройство с новой информацией.
очевидно, что для устройств iOS 5.0 вы, вероятно, захотите обнаружить измененные файлы для устройств pre-iOS 5.0 в своем собственном облаке, а также иметь возможность общаться с iCloud.
не похоже, что вы боретесь с проблемой iCloud/notICloud так же, как с проблемой iOS5/notIOS5.
Если ваша цель развертывания-iOS5, то просто всегда используйте структуру UIDocument. Если он вездесущ, то ваш NSMetaDataQuery найдет его в облаке; если нет, он найдет его на устройстве.
Если, с другой стороны, вы хотите предоставить pre 5.0 доступ к вашему приложению, то вам нужно будет условно проверить, работает ли iOS 5.0 или более значительный. Если это так, то используйте UIDocument; если нет, то считывайте / записывайте данные старым способом.
мой подход состоял в том, чтобы написать условный метод saveData, который проверяет iOS5. Если он существует, Я обновляю счетчик изменений (или использую диспетчер отмены). В вашем случае textViewDidChange будет вызывать этот метод. Если нет, то он сохраняет на диск-старому. При загрузке происходит обратное.
вы озадачены "обрабатывать файлы в iCloud так же, как вы обрабатываете все другие файлы в песочнице приложения."Это справедливо для чего-то вроде Keynote и Numbers, где вы храните кучу файлов, и если у вас есть iCloud, они начинают синхронизироваться волшебным образом.
тем не менее, вы создаете что-то, что зависит от функциональности iCloud. Вы не можете придерживаться этого утверждения, потому что ваше приложение зависит от iCloud, чтобы все работало так, как оно должно. Вы будете либо вам нужно закрыть свое приложение и просто сказать: "пожалуйста, настройте iCloud, чтобы это работало" или дублируйте функциональность iCloud (свою или чужую), которую вы всегда можете использовать, независимо от этого.
Comments