एक शुरुआत के रूप में, मैं iCloud के साथ संघर्ष कर रहा हूं। कुछ नमूने हैं, लेकिन वे आमतौर पर काफी विस्तृत हैं (डेवलपर मंच पर आईक्लाउड और कोरडाटा के लिए एक है जो बड़े पैमाने पर है)। सेब डॉक्स ठीक कर रहे हैं, लेकिन मैं अभी भी बड़ी तस्वीर नहीं देख सकता। तो कृपया मेरे साथ सहन करें, इनमें से कुछ प्रश्न काफी मौलिक हैं, लेकिन उत्तर देने में संभवतः आसान हैं।
संदर्भ: मेरे पास एक बहुत ही सरल आईक्लाउड ऐप चल रहा है (नीचे पूर्ण नमूना कोड)। उपयोगकर्ता को केवल एक UITextView दिखाया गया है और उसके इनपुट को text.txt नामक फ़ाइल में सहेजा गया है।
Txt फ़ाइल को क्लाउड पर धकेल दिया जाता है और सभी उपकरणों के लिए उपलब्ध कराया जाता है। पूरी तरह से काम करता है, लेकिन:
मुख्य समस्या: उन उपयोगकर्ताओं के बारे में जो iCloud का उपयोग नहीं करते हैं?
जब मैं अपना ऐप लॉन्च करता हूं (नीचे कोड देखें), मैं जांचता हूं कि क्या उपयोगकर्ता के पास आईक्लाउड सक्षम है। यदि iCloud सक्षम है, तो सब कुछ ठीक है। ऐप आगे बढ़ता है और क्लाउड में text.txt की तलाश करता है। यदि पाया जाता है, तो वह इसे लोड करेगा और उपयोगकर्ता को प्रदर्शित करेगा। यदि text.txt क्लाउड में नहीं पाया जाता है, तो यह बस एक नया text.txt बनाएगा और उपयोगकर्ता को प्रदर्शित करेगा।
यदि उपयोगकर्ता के पास iCloud सक्षम नहीं है, तो कुछ भी नहीं होगा। मैं यह कैसे संभव करूंगा कि गैर-iCloud उपयोगकर्ता अभी भी मेरे टेक्स्ट ऐप के साथ काम कर सकें? या मैं बस उन्हें अनदेखा करूँ? क्या मुझे गैर-आईक्लाउड उपयोगकर्ताओं के लिए अलग-अलग कार्य लिखने की आवश्यकता होगी? Ie फ़ंक्शन जिसमें मैं दस्तावेज़ फ़ोल्डर से एक text.txt लोड करता हूं?
ICloud में फ़ाइलों का इलाज उसी तरह से करें जैसे आप अपने ऐप सैंडबॉक्स में अन्य सभी फ़ाइलों का इलाज करते हैं।
हालाँकि, मेरे मामले में अब कोई 'सामान्य' ऐप सैंडबॉक्स नहीं है। यह बादल में है। या क्या मैं हमेशा डिस्क से अपना text.txt लोड करता हूं और फिर iCloud से जांचता हूं कि क्या कुछ और अप-टू-डेट है?
संबंधित समस्या: फ़ाइल संरचना - सैंडबॉक्स बनाम क्लाउड
शायद मेरी मुख्य समस्या एक गलतफहमी है कि iCloud को कैसे काम करना चाहिए। जब मैं UIDocument का एक नया उदाहरण बनाता हूं, तो मुझे दो विधियों को अधिलेखित करना होगा। पहले - (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)outError
क्लाउड से फाइल प्राप्त करने के लिए और फिर -(id)contentsForType:(NSString *)typeName error:(NSError **)outError
क्लाउड में फाइल प्राप्त करने के लिए।
क्या मुझे अलग-अलग कार्यों को शामिल करना होगा जो मेरे सैंडबॉक्स में text.txt की एक स्थानीय प्रतिलिपि भी बचाएगा? क्या यह गैर-आईक्लाउड उपयोगकर्ताओं के लिए काम करेगा? जैसा कि मैं iCloud को समझता हूं, यह text.txt की एक स्थानीय प्रतिलिपि को स्वचालित रूप से बचाएगा। इसलिए मुझे अपने ऐप के 'पुराने' सैंडबॉक्स में कुछ भी सेव करने की कोई आवश्यकता नहीं होनी चाहिए (जैसे कि यह पुराने, प्री-आईक्लाउड दिनों में हुआ करता था)। अभी, मेरा सैंडबॉक्स पूरी तरह से खाली है, लेकिन मुझे नहीं पता कि यह सही है या नहीं। क्या मुझे वहाँ में text.txt की एक और कॉपी रखनी चाहिए? यह मेरी डेटा संरचना को अव्यवस्थित करने जैसा लगता है ... जैसे कि क्लाउड में एक टेक्स्ट.टैक्स है, एक मेरे डिवाइस पर आईक्लाउड सैंडबॉक्स में है (जो कि ऑफ़लाइन होने पर भी काम करेगा), और अच्छे पुराने सैंडबॉक्स में एक तीसरा मेरा ऐप ...
मेरा कोड: एक साधारण iCloud नमूना कोड
यह शिथिल रूप से एक उदाहरण पर आधारित है जो मुझे डेवलपर फोरम में और डब्ल्यूडब्ल्यूडीसी सत्र वीडियो पर मिला। मैंने उसे नंगे न्यूनतम तक छीन लिया। मुझे यकीन नहीं है कि मेरी एमवीसी संरचना कोई अच्छी है। मॉडल AppDelegate में है जो आदर्श नहीं है। इसे बेहतर बनाने के किसी भी सुझाव का स्वागत है।
संपादित करें: मैंने मुख्य प्रश्न निकालने की कोशिश की और इसे [यहाँ] पोस्ट किया। 4
अवलोकन:
सबसे महत्वपूर्ण बिट जो क्लाउड से text.txt को लोड करता है:
// 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];
}