I am trying to use iCloud to store my app's userSetting, here is my Save & Load Code: , it usually doing fine but sometimes crash with message like: attempt to open or a revert document that already has an open or revert operation in flight or send to dealloc instance so i add fileState logs befere openWithCompletionHandler it always show state = UIDocumentStateClosed no matter will crash or not, i save data when applecationDidEnterBackground and load when applicationDidBecomeActive.
save:
-(void)storeToiCloud{
NSURL *baseURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (baseURL) {
NSURL *documentsURL = [baseURL URLByAppendingPathComponent:#"Documents"];
NSURL *documentURL = [documentsURL URLByAppendingPathComponent:[NSString stringWithFormat:#"userSetting"]];
if (!loadDocument) {
self.loadDocument = [[MyUserDefaultsDocument alloc] initWithFileURL:documentURL];
}
loadDocument.myUserDefault = [MyUserDefaults standardUserDefaults];
[loadDocument saveToURL:documentURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
}];
}
}
load:
-(BOOL)shouldSynciCloud{
if (![Utility iCloudEnable]) {
return NO;
}
NSURL *baseURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (baseURL) {
self.query = [[[NSMetadataQuery alloc] init] autorelease];
[self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == 'userSetting'", NSMetadataItemFSNameKey];
[self.query setPredicate:predicate];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(queryDidFinish:) name:NSMetadataQueryDidFinishGatheringNotification object:self.query];
[self.query startQuery];
[Utility showSpinner];
return YES;
}
return NO;
}
- (void)queryDidFinish:(NSNotification *)notification {
NSMetadataQuery *query = [notification object];
// Stop Updates
[query disableUpdates];
// Stop Query
[query stopQuery];
[query.results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSURL *documentURL = [(NSMetadataItem *)obj valueForAttribute:NSMetadataItemURLKey];
if([[documentURL lastPathComponent] hasPrefix:#"userSetting"]){
self.document = [[MyUserDefaultsDocument alloc] initWithFileURL:documentURL];
NSString* message;
if (document.documentState == UIDocumentStateNormal){
message = #"UIDocumentStateNormal";
}else if (document.documentState == UIDocumentStateClosed) {
message = #"UIDocumentStateClosed";
}else if(document.documentState == UIDocumentStateEditingDisabled){
message = #"UIDocumentStateEditingDisabled";
}else if(document.documentState == UIDocumentStateInConflict){
message = #"UIDocumentStateInConflict";
}else if(document.documentState == UIDocumentStateSavingError){
message = #"UIDocumentStateSavingError";
}
NSLog(#"state = %#",message);
[document openWithCompletionHandler:^(BOOL success) {
if (success) {
MyUserDefaults *prefs = [MyUserDefaults standardUserDefaults];
NSData *book =[document.myUserDefault.realDict objectForKey:#"realbook"];
NSData *readSetting = [document.myUserDefault.realDict objectForKey:#"epubRS"];
if (book&&[[NSUserDefaults standardUserDefaults] boolForKey:#"iCloudBook"]) {
[prefs setObject:book forKey:#"realbook"];
[Utility reloadRealBooks];
}
if (readSetting&&[[NSUserDefaults standardUserDefaults] boolForKey:#"iCloudSetting"]) {
[prefs setObject:readSetting forKey:#"epubRS"];
[Utility setEpubReadSettingFromData:readSetting];
}
[prefs save];
[[NSNotificationCenter defaultCenter]postNotificationName:#"iCloudSynced" object:nil];
[Utility removeSpinner];
}
else{
[[NSNotificationCenter defaultCenter]postNotificationName:#"iCloudSyncfailed" object:nil];
[Utility removeSpinner];
}
}];
}
}];
if ([query.results count]==0) {
[[NSNotificationCenter defaultCenter]postNotificationName:#"iCloudSyncfailed" object:nil];
[Utility removeSpinner];
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:nil];
}
As noted in this question, the error occurs if your app attempts to call [document openWithCompletionHandler:] method twice in close succession.
Because the openWithCompletionHandler: opens the document asynchronously, the document may still be opening when the method is called again.
If this happens, your app ends up trying to open the document twice (as the document state will remain UIDocumentStateClosed until completion) and this causes the exception to be thrown.
Related
My app working fine when app is in background and open it by clicking on Push Notification. But if I terminate app and open it through push notification then app crash. I also integrate Crashlytics. Here is attached photo & code. App crash only in one type of notification.It is not data parsing issue.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error) {
NSLog(#"request authorization succeeded!");
//[self showAlert];
}
}];
// Branch Intigration
Branch *branch = [Branch getInstance];
// if is debug
// [[Branch getInstance] setDebug];
[branch initSessionWithLaunchOptions:launchOptions andRegisterDeepLinkHandler:^(NSDictionary *params, NSError *error) {
if (!error && params) {
DecisionController * mainVC = [[DecisionController alloc] init];
[mainVC viewDidLoad];
// params are the deep linked params associated with the link that the user clicked -> was re-directed to this app
// params will be empty if no data found
// ... insert custom logic here ...
NSString * non_branch_link = [params objectForKey:#"+clicked_branch_link"];
if ([[params objectForKey:#"+clicked_branch_link"] boolValue]) {
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:#"DeepLinkNotification" object:self userInfo:params];
}
NSLog(#"params: %#", params.description);
}
}];
// MAGICAL RECORD Intigration
[MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:#"EmergencMeDatabase.sqlite"];
// [Fabric with:#[[branch class]]];
NSLog(#"magical record path to SQLite base %#", [NSPersistentStore MR_defaultLocalStoreUrl]);
[Fabric with:#[[Crashlytics class]]];
[SharedRenderer setValue:#"NO" key:isForRedar];
[[NSUserDefaults standardUserDefaults] setFloat:0 forKey:#"lastnotificationminute"];
[[FBSDKApplicationDelegate sharedInstance] application:application
didFinishLaunchingWithOptions:launchOptions];
[MPNotificationView registerNibNameOrClass:#"CustomNotificationView"
forNotificationsOfType:#"Custom"];
[self registerForRemoteNotifications];
[SharedRenderer sharedInstance].retryCount = 0;
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"generatenotification"];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"FirstSubscribe"];
mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone"
bundle: nil];
if (launchOptions[UIApplicationLaunchOptionsLocationKey]) {
[self startLocationManager:#""];
}
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
[SharedRenderer setValue:#"0" key:#"alldatafetched"];
[SharedRenderer setValue:#"" key:#"currentchannel"];
NSMutableArray* alerts = [[NSMutableArray alloc] init];
NSData* alertsData=[NSKeyedArchiver archivedDataWithRootObject:alerts];
[[NSUserDefaults standardUserDefaults] setObject:alertsData forKey:#"nonreasonalerts"];
NSMutableArray* receivedAlerts = [[NSMutableArray alloc] init];
NSData* receivedAlertsData=[NSKeyedArchiver archivedDataWithRootObject:receivedAlerts];
[[NSUserDefaults standardUserDefaults] setObject:receivedAlertsData forKey:#"receivedalerts"];
NSMutableArray* myAlerts = [[NSMutableArray alloc] init];
NSData* myAlertsData=[NSKeyedArchiver archivedDataWithRootObject:myAlerts];
[[NSUserDefaults standardUserDefaults] setObject:myAlertsData forKey:#"myalerts"];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"OB"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
if (![[SharedRenderer getValueFor:#"isLogin"] boolValue])
{
navigationController = [mainStoryboard instantiateViewControllerWithIdentifier:#"loginVC"];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"showhomealert"];
}
else
{
if ([[SharedRenderer getValueFor:#"family_id"] isEqualToString:#""] || [[SharedRenderer getValueFor:#"alldatafetched"] isEqualToString:#"0"])
{
navigationController = [mainStoryboard instantiateViewControllerWithIdentifier:#"Smarticestep1"];
}
else
{
navigationController = [mainStoryboard instantiateViewControllerWithIdentifier:#"homenav"];
[self activatedeacAlertTimer];
[self activatePendingAlertTimer];
[self checkFor_AlertDeactivation];
[self fetchInvitationCount];
// Mark by Mian:
// [self updatePSAlertTable];
}
}
[self.window setRootViewController: navigationController];
[self.window makeKeyAndVisible];
LeftMenuViewController *leftMenu = (LeftMenuViewController*)[mainStoryboard
instantiateViewControllerWithIdentifier: #"LeftMenuViewController"];
RightMenuViewController *rightMenu = (RightMenuViewController*)[mainStoryboard
instantiateViewControllerWithIdentifier: #"RightMenuViewController"];
[SlideNavigationController sharedInstance].rightMenu = rightMenu;
[SlideNavigationController sharedInstance].leftMenu = leftMenu;
[SlideNavigationController sharedInstance].menuRevealAnimationDuration = .4;
// Creating a custom bar button for right menu
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[button setImage:[UIImage imageNamed:#"gear"] forState:UIControlStateNormal];
[button addTarget:[SlideNavigationController sharedInstance] action:#selector(toggleRightMenu) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
[SlideNavigationController sharedInstance].rightBarButtonItem = rightBarButtonItem;
[[NSNotificationCenter defaultCenter] addObserverForName:SlideNavigationControllerDidClose object:nil queue:nil usingBlock:^(NSNotification *note)
{
// NSString *menu = note.userInfo[#"menu"];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:SlideNavigationControllerDidOpen object:nil queue:nil usingBlock:^(NSNotification *note)
{
// NSString *menu = note.userInfo[#"menu"];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:SlideNavigationControllerDidReveal object:nil queue:nil usingBlock:^(NSNotification *note)
{
// NSString *menu = note.userInfo[#"menu"];
}];
[self ConnectPusher];
if ([[SharedRenderer getValueFor:#"isLogin"] boolValue]){
[self checkFor_AlertDeactivation];
[self removePastAlert];
[self deactivateAlert];
[self goOnline_Offline:#"1"];
}
[self showAlert:launchOptions];
return YES;
}
The issue is that you get the notification that opens the app from launchOptions of didFinishLaunchingWithOptions method and deliver to the same function that handles it when app is opened and perform a push / present that makes the app crash as windows's root view controller is not yet set , so delay that until app it being active
If my app is running on foreground or background this working fine. I am receiving the notifications and save it on the local database. But if the app killed from the background it receives the remote notifications but the following method is not called. And the issue is if I tap any one of the notification,only that notification will saved on the local database.
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{
[PFPush handlePush:userInfo];
NSLog(#"Received notification: %#", userInfo);
NSString *alertString = [[userInfo objectForKey:#"aps"]valueForKey:#"alert"];
NSLog(#"%#",alertString);
NSString *msgType = [userInfo objectForKey:#"messageType"];
NSString *senderId = [userInfo objectForKey:#"senderId"];
NSString *receverId = [userInfo objectForKey:#"receverId"];
NSString *msg = [userInfo objectForKey:#"message"];
NSString *timeStr = [userInfo objectForKey:#"Time"];
NSLog(#"msg type%# senderId %# receverId %# message %#",msgType,senderId,receverId,msg);
if ([AppDelegate isNetworkReachable]){
if ([msgType isEqualToString:#"CHAT"]) {
Chatmessage *Cmsg=[[Chatmessage alloc]init];
Cmsg.chat_date =timeStr;
Cmsg.chat_image =#"";
Cmsg.chat_message = msg;
Cmsg.chat_Receiver_Id = receverId;
Cmsg.chat_Sender_Id = senderId;
NSLog(#"recid%#",Cmsg.chat_Receiver_Id);
NSMutableArray *arryMsg = [[NSMutableArray alloc]init];
arryMsg = [[DBModelNew database]getChatMessageBasedOnTime:receverId SenId:senderId time_stamp:timeStr message:msg];
if (arryMsg.count == 0) {
[[DBModelNew database]insertmsg:Cmsg];
}
[[NSNotificationCenter defaultCenter]postNotificationName:#"receivedmessage" object:nil];
chatHistory *chatObj = [[chatHistory alloc]init];
chatObj.chat_meta_id = [NSString stringWithFormat:#"%#",senderId];
chatObj.last_send_message = msg;
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm:ss";
NSString *str=[dateFormatter stringFromDate:[NSDate date]];
chatObj.last_time_stamp = [self dateformat:str];
PFQuery *query = [PFUser query];
[query whereKey:#"objectId" equalTo:senderId];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (NSDictionary *dict in objects) {
[[dict objectForKey:#"ProfilePic"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error) {
if (data) {
UIImage *image = [UIImage imageWithData:data];
if (image) {
chatObj.fndimage = image;
chatObj.name = [dict objectForKey:#"name"];
[[DBModelNew database]insertChat:chatObj];
[[NSNotificationCenter defaultCenter]postNotificationName:#"receivedNewMessage" object:nil];
}
}
}
}];
}
}
}];
}
}
}
From the Apple docs, if the user hard closes the app it does not call the method.
In addition, if you enabled the remote notifications background mode,
the system launches your app (or wakes it from the suspended state)
and puts it in the background state when a remote notification
arrives. However, the system does not automatically launch your app if
the user has force-quit it. In that situation, the user must relaunch
your app or restart the device before the system attempts to launch
your app automatically again.
If you want to launch specific payload dictionary from viewDidLoad then you simply call the following :
UILocalNotification *localNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
And you get the userInfo this way:
NSString *msgType = [localNotif objectForKey:#"messageType"];
And now you can act accordingly. This is just for the circumstances you stated in your title. When the app is not 'running' (terminated)
I have a label and I need it to show the score, coins ect on every ViewController, That means when The score changes it changes every where throughout the whole app...
I've tried to set a label to show score on the whole app but I cant figure out how!
Please help
This is what I have so Far In the View Controller:
-(void)viewDidLoad
{
{
[super viewDidLoad];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //1
NSString *documentsDirectory = [paths objectAtIndex:0]; //2
path = [documentsDirectory stringByAppendingPathComponent:#"SettingsList.plist"]; //3
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path]) //4
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"SettingsList"ofType:#"plist"]; //5 //5
[fileManager copyItemAtPath:bundle toPath: path error:&error]; //6
}
savedStock = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
nPoint = [[savedStock objectForKey:#"point"] intValue];
[giftAmount setText:[NSString stringWithFormat:#"%d",nPoint]];
[self updateCurrencyBalance];
[self zoneLoading];
}
//adcolony
- (void) viewDidAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateCurrencyBalance) name:kCurrencyBalanceChange object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(zoneReady) name:kZoneReady object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(zoneOff) name:kZoneOff object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(zoneLoading) name:kZoneLoading object:nil];
}
// Get currency balance from persistent storage and display it
- (void)updateCurrencyBalance {
NSNumber* wrappedBalance = [[NSUserDefaults standardUserDefaults] objectForKey:kCurrencyBalance];
NSUInteger balance = wrappedBalance && [wrappedBalance isKindOfClass:[NSNumber class]] ? [wrappedBalance unsignedIntValue] : 0;
[giftAmount setText:[NSString stringWithFormat:#"%u", balance]];
[savedStock setObject:[NSNumber numberWithFloat:nPoint = balance] forKey:#"point"];
[savedStock writeToFile: path atomically:YES];
}
I have an action in the other PlayViewController which (minusus) -200 coins, but its not updating in the ViewController?
One way is to use NSNotificationCenter.
Add this code to all the places that change the value of your score:
- (void)updateScore:(NSNumber *)newValue
// update the score
self.score = newValue;
// create an dictionary object containing the score to be sent with the notification
NSMutableDictionary* userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:self.score forKey:#"score"];
// Add this to send a notification to all the listeners in the whole app
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationScoreChanged" object:nil userInfo:userInfo];
}
In the viewDidLoad methods of your view controllers, add this code:
- (void)viewDidLoad:(BOOL)animated
{
[super viewDidLoad:animated];
// Add this code to start receiving notifications (become a listener)
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(scoreChanged:) name:#"NotificationScoreChanged" object:nil];
}
Then somewhere in your view controllers, add this method to update your UI:
- (void)scoreChanged:(NSNotification *)notification
{
// retrieve the score results
if ([notification.name isEqualToString:#"NotificationScoreChanged"])
{
NSDictionary *userInfo = notification.userInfo;
NSNumber *score = [userInfo objectForKey:#"score"];
// and update your labels
self.scoreLabel.text = [score description];
}
And in your view controllers, add dealloc:
- (void)dealloc
{
//Unregister yourself (stop listening)
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Note: you should adapt the code depending on how you store and retrieve your score. Ie, if you use NSUserDefaults (see #erid's answer), CoreDate, etc.
Use NSUserDefaults for storing data (Data gets deleted only when program is removed from iOS Device or you can remove it manually).
Storing Value on NSUserDefaults
//text is your label.text, and each time you change it, save it to user details
NSString *text;
//Store them to NSUserDefaults with a specific key
[[NSUserDefaults standardUserDefaults] setObject:text forKey:#"label"];
Getting value back
NSString *textValue = [[NSUserDefaults standardUserDefaults] objectForKey:#"label" ];
label.text = textValue;
You can try adding NSNotification to your program to notificate you when the NSString you set to change the label changes, and there you can set this value to NSUserDefaults.
As David said this is not the best way to do this, but you have to read more about Singletons if you need to save your DATA until the app is closed.
Hope it helps
i just released my app and realized that NSUbiquitousKeyValueStoreDidChangeExternallyNotification is not fired.
I need it that it is fired the first time a user open the app to register him (and check if it was already a user before).
The problem is that, if the notification isn't fired, then my app will be stuck.
I'm using key-value and it's properly setted in Xcode
Now, with my iphone, with developer profile, everything works great.
With a distribution profile, it does not.
I already have a production profile with icloud enabled (and app id with icloud enabled too).
In my app, this two parameters,even in the app store version, are fine:
_currentiCloudToken = [[NSFileManager defaultManager] ubiquityIdentityToken];
_myUbiquityContainer = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier: nil];
Here's my code:
- (id)initWithCloudStore:(NSUbiquitousKeyValueStore *) store {
if ((self = [super init])) {
_iCloudStore = store;
_currentiCloudToken = [[NSFileManager defaultManager] ubiquityIdentityToken];
_myUbiquityContainer = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier: nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(storeDidChange:)
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
object:_iCloudStore];
}
return self;
}
-(BOOL) iCloudIsEnabled {
if(_currentiCloudToken && _myUbiquityContainer != nil) return TRUE;
else return FALSE;
}
-(void) sync {
if([_iCloudStore synchronize]) NSLog(#"iCloud Sync OK");
}
-(void) storeDidChange:(NSNotification*)notification {
NSString *iCloudUUID = [_iCloudStore objectForKey:userUUIDKey];
if(iCloudUUID != nil) {
NSLog(#"iCloud UUID present with value %#",iCloudUUID);
//[[iCloudHelper sharedInstance] removeObjectForKey:userUUIDKey];
// sostituisco uuid locale con quello di icloud
[[NSUserDefaults standardUserDefaults] setObject:iCloudUUID forKey:userUUIDKey];
[[NSUserDefaults standardUserDefaults] synchronize];
} else {
// salvo uuid in iCloud
NSLog(#"UUID not present on iCloud");
[_iCloudStore setObject:[self createNewUUID] forKey:userUUIDKey];
}
int reason = [[[notification userInfo] objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey] intValue];
if(reason == NSUbiquitousKeyValueStoreInitialSyncChange) {
NSLog(#"This was the initial sync");
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"iCloudDone" object:nil];
}
What I'm missing?
I have an issue where I can't seem to dealloc UIDocument (used in iCloud)
After running an NSMetaDataQuery to look for the document as follows..
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
_query = query;
[query setSearchScopes:[NSArray arrayWithObject:
NSMetadataQueryUbiquitousDocumentsScope]];
NSPredicate *pred = [NSPredicate predicateWithFormat:
#"%K == %#", NSMetadataItemFSNameKey, kFILENAME];
[query setPredicate:pred];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(queryDidFinishGathering:)
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
[query startQuery];
I process my query
- (void)queryDidFinishGathering:(NSNotification *)notification {
NSMetadataQuery *query = [notification object];
[query disableUpdates];
[query stopQuery];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
_query = nil;
[self loadData:query];
}
Then load or create a new document.
- (void)loadData:(NSMetadataQuery *)query {
if ([query resultCount] == 1) {
NSMetadataItem *item = [query resultAtIndex:0];
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
MyDocument *doc = [[[MyDocument alloc] initWithFileURL:url] autorelease];
[doc openWithCompletionHandler:^(BOOL success) {
if (success) {
NSLog(#"iCloud document opened %#", doc);
[doc updateChangeCount:UIDocumentChangeDone];
} else {
NSLog(#"failed opening document from iCloud");
}
}];
} else {
NSURL *ubiq = [[NSFileManager defaultManager]
URLForUbiquityContainerIdentifier:nil];
NSURL *ubiquitousPackage = [[ubiq URLByAppendingPathComponent:
#"Documents"] URLByAppendingPathComponent:kFILENAME];
MyDocument *doc = [[[MyDocument alloc] initWithFileURL:ubiquitousPackage] autorelease];
[doc saveToURL:[doc fileURL]
forSaveOperation:UIDocumentSaveForCreating
completionHandler:^(BOOL success) {
if (success) {
[doc openWithCompletionHandler:^(BOOL success) {
NSLog(#"new document opened from iCloud");
[doc updateChangeCount:UIDocumentChangeDone];
}];
}
}];
}
}
The NSLog(#"iCloud document opened %#", doc); shows a different memory address for each UIDocument.
I have an NSLog in my UIDocument subclass, it never gets called. I cannot see where it is being retained that I am not releasing it. This query is ran whenever I want to sync my cloud data, this happens fairly regularly. The data syncs correctly.
I am experiencing strange crashes where my app will simply close to the dashboard, with nothing in the debug (from previous experiences I know this often to be the app expending too much memory and being terminated.)
I think that my UIDocument is leaking, would I be correct in this assumption, this is the first time i've wrestled with iCloud so I'm still in the dark over a few things.
My subclass has the following properties:
#property (copy, nonatomic) NSData *infoData;
#property (copy, nonatomic) NSMutableArray *firstArray;
#property (copy, nonatomic) NSMutableArray *secondArray;
#property (copy, nonatomic) NSMutableArray *thirdArray;
I am not using ARC.
I did not realise that I had to do this:
[doc updateChangeCount:UIDocumentChangeDone];
[doc closeWithCompletionHandler:nil];
Obviously if a file is open for writing, then it would not be wise to allow it to be deallocated!
Doh! Hopefully this saves someone some time in the future.