Proper way to detect deletion of documents in iCloud across devices? - ios

I can't find an obvious way to detect deletion of a document that I'm monitoring (I cache a list of docs my app cares about).
On Device1:
When I delete a doc, I call
[fileCoordinator coordinateWritingItemAtURL:fileURL options:NSFileCoordinatorWritingForDeleting
error:&err byAccessor:^(NSURL* writingURL) {
[fileManager removeItemAtURL:writingURL error:nil];
This works fine on Device1, everything stays in synch.
On Device2:
I was trying to rely on NSMetadataQuery notifications:
Initial file list is coming in fine on NSMetadataQueryDidFinishGatheringNotification
Document adds/changes are coming in fine via NSMetadataQueryDidUpdateNotification
When i delete a file on Device1, I get a strange result: an update comes in NSMetadataQueryDidUpdateNotification with all my docs (except the one deleted) listed
I'm not sure how I am supposed to detect that the missing file was deleted or that the update notification was for that purpose
Question 1: What should I be checking for?
I tried another route which was to register as a NSFilePresenter for the iCloud URL:
- (NSURL *)iCloudDocumentsURL
{
return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] URLByAppendingPathComponent:#"Documents"];
}
I now get called via the NSFilePresenter protocol when files in that URL change, but it's punitively slow to run the logic to determine the missing doc since the callback is vague.
The only call I get back is - (void)presentedItemDidChange;
I was expecting to get a callback via - (void)accommodatePresentedSubitemDeletionAtURL:(NSURL *)url completionHandler:(void (^)(NSError *errorOrNil))completionHandler
But this method is never called.
Question 2: Any idea how to get that working?

Since you're keeping track of files previously reported from iCloud, the deleted file is absent from the current iCloud listing, and so you just compare the two lists to find which was deleted.
That's what I'm doing in my "file manager" view controller, because I keep a NSMutableDictionary of file entries, which includes among other things, keys for the positions of the file "icons" in my master view. When I get notified of an update, and that update results in either more or fewer files, I animate the icon changes based upon those file
changes.

tobinjim is correct in that the NSMetadataQuery returns the entire result set every time. I had expected that only changes to be sent to save bandwidth, but I did not RTFM correctly.
Once I figured that out, I ran into a bug in iOS libraries. This was the crash that was occurring when I deleted a document on one device and the iCloud query update came across to my other device:
2012-06-25 13:15:12.343 app[19779:707] *** -[NSMutableIndexSet indexGreaterThanOrEqualToIndex:]: message sent to deallocated instance 0xdaae2c0
(gdb) bt
#0 0x31937870 in ___forwarding___ ()
#1 0x31892650 in __forwarding_prep_0___ ()
#2 0x373cc676 in __NSIndexSetEnumerate ()
#3 0x373a1ee8 in -[NSIndexSet enumerateIndexesWithOptions:usingBlock:] ()
#4 0x371c1f08 in -[LBQuery _processUpdates] ()
#5 0x373571a6 in -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] ()
#6 0x3737ffa4 in -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:] ()
#7 0x371c2274 in -[LBQuery processUpdates] ()
#8 0x373a88d6 in -[NSMetadataQuery _update] ()
I believe I have found the cause of this issue. I was reviewing the new IOS 6 sample code from the presentation by Luke the Hiesterman. I noticed that there were no calls to NSMetadataQuery disable/enableUpdates like there were in the previous iCloud sample apps. I removed all calls to these. I also changed my method which handled NSMetadataQueryDidUpdateNotification calls to run asynchronously but in a queued manner.
There seems to be a race condition occurring between threads for NSMetadataQuery. In some calls the results of the query are fine but at other times, they have been released and the results show up as NSZombie's (thanks to gdb which is way way better than the current lldb). So the enabling and disabling of the query across threads causes the LBQuery to crash by calling objects that have been released.
The removal of all the enabling and disabling for NSMetadataQuery seems to have sped up my app as well, and iCloud seems much more stable.

Since iOS 8 NSMetadataQuery extended NSMetadataQueryDidUpdateNotification userInfo with NSMetadataQueryUpdateRemovedItemsKey which you can use to detect which files were removed.
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSMetadataQuery_Class/#//apple_ref/doc/constant_group/Keys_for_Use_with_a_Notification_Info_Dictionary

Related

WatchKit extension crash: "Program ended with exit code: 0"

For people wanting to reply quickly without reading the post: I am not hitting any memory limits. Read the whole post for details.
My WatchKit extension cannot properly function without the user first being "onboarded" through the phone app. Onboarding is where the user must accept the permissions that we require, so it's very crucial.
On my WatchKit extension, I wanted to display a simple warning for users who had not finished onboarding within our phone app yet.
As such, I thought I'd get the status of onboarding from the phone in two ways:
When the user opens the app/the app is activated (I use the willActivate method to detect this)
When the app finishes onboarding it sends a message to the watch of its completion (if the extension is reachable, of course)
Both of these combined would ensure that the status of onboarding is always kept in sync with the watch.
I wrote the first possibility in, utilizing reply handlers to exchange the information. It worked just fine, without any troubles. The warning telling the user to complete disappears, the extension does not crash, and all is well.
I then wrote in the second possibility, of the extension being reachable when the user finishes onboarding (with the phone then directly sending the companion the new status of onboarding). My extension crashes when it receives this message, and I am stuck with this odd error.
Program ended with exit code: 0
My extension does not even get a chance to handle the new onboarding status, the extension just quits and the above error is given to me.
I am not hitting any sort of memory limit. I have read the technical Q&A which describes what a memory usage limit error looks like, and I don't receive any sort of output like that whatsoever. As well, before the extension should receive the message, this is what my memory consumption looks like.
I have monitored the memory consumption of the extension right after finishing onboarding, and I see not a single spike indicating that I've gone over any kind of threshold.
I have tried going line by line over the code which manages the onboarding error, and I cannot find a single reason that it would crash with this error. Especially since the reply handler method of fetching the onboarding status works so reliably.
Here is the code of how I'm sending the message to the watch.
- (void)sendOnboardingStatusToWatch {
if(self.connected){
[self.session sendMessage:#{
LMAppleWatchCommunicationKey: LMAppleWatchCommunicationKeyOnboardingComplete,
LMAppleWatchCommunicationKeyOnboardingComplete: #(LMMusicPlayer.onboardingComplete)
}
replyHandler:nil
errorHandler:^(NSError * _Nonnull error) {
NSLog(#"Error sending onboarding status: %#", error);
}];
}
}
(All LMAppleWatchCommunicationKeys are simply #define'd keys with exactly their key as the string value. ie. #define LMAppleWatchCommunicationKey #"LMAppleWatchCommunicationKey")
Even though it's never called by the extension, here is the exact receiving code of the extension which handles the incoming data, if it helps.
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message {
NSString *key = [message objectForKey:LMAppleWatchCommunicationKey];
if([key isEqualToString:LMAppleWatchCommunicationKeyOnboardingComplete]){
BOOL newOnboardingStatus = [message objectForKey:LMAppleWatchCommunicationKeyOnboardingComplete];
[[NSUserDefaults standardUserDefaults] setBool:newOnboardingStatus
forKey:LMAppleWatchCommunicationKeyOnboardingComplete];
dispatch_async(dispatch_get_main_queue(), ^{
for(id<LMWCompanionBridgeDelegate> delegate in self.delegates){
if([delegate respondsToSelector:#selector(onboardingCompleteStatusChanged:)]){
[delegate onboardingCompleteStatusChanged:newOnboardingStatus];
}
}
});
}
}
Before including this onboarding-related code, my WatchKit extension was tested by over 100 people, without any troubles. I am using the exact same custom error dialogue that I was using before, just with a different string. I cannot for the life of me figure out what is causing this crash, and the ambiguity of it has given me very little to work with.
Any help would be greatly appreciated. Thank you very much for taking your time to read my post.
Edit: I just tried creating a symbolic breakpoint for exit(), which is never hit. If I call exit() myself, it calls the breakpoint, so I know the breakpoint itself is working.

CoreData ContextObjectsDidChangeNotification called from another thread

Recently while investigating some weird UI behavior of UIPickerView, I reached the conclusion that is was because I was reloading its components in a selector that was called observing the NSManagedObjectContextObjectsDidChangeNotification. The thing is, At least I was pretty sure this will be always be called from the main thread. But I was wrong. I'm using UIManagedDocument, and sometimes I get the following notification from another thread:
NSConcreteNotification 0x14a2664b0 {name = NSObjectsChangedInManagingContextNotification; object = <NSManagedObjectContext: 0x14a3e1be0>; userInfo = {
invalidatedAll = (
"0xd00000000d5c000e <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/AdditionalAssetAttributes/p855>",
"0xd00000000d58000e <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/AdditionalAssetAttributes/p854>",
"0xd00000000d5c000c <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/Asset/p855>",
"0xd00000000d58000c <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/Asset/p854>",
"0xd000000001d40018 <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/SidecarFile/p117>",
"0xd000000001d00018 <x-coredata://B12EF0BD-E372-44D2-AF2F-8E28C5EF3E00/SidecarFile/p116>"
);
managedObjectContext = "<NSManagedObjectContext: 0x14a3e1be0>";
My question is: Does anyone knows why this notification is being called? What is the purpose of the invalidateAll elements. Also I have no Idea what AdditionalAssetAttributes or SidecarFile are.
Thanks!
I'm seeing this notification as well regularly as of iOS 11. There's not much documentation, but it seems if the notification's userInfo contains the key NSInvalidatedAllObjectsKey (invalidatedAll), then you must refetch all NSManagedObject's from the document that your application has references too. Failure to do see leaves them all no longer usable. The contents of the array can be ignored (e.g. I often find it is empty)

Saving NSManagedObjectContext on Stackmob -- uploads to server, but crashes app

I have been running into an issue in using StackMob as the backend of my iOS application (though I'm not sure if this is an issue in wrongly using StackMob's methods or an iOS issue).
I am allowing a user to create a post object that is just a subclassed NSManagedObject, and uploading that to the server to be used in other parts of the application. The issue that arises occurs in the method:
[NSManagedObjectContext saveOnSuccess:<^(void)successBlock> onFailure:<^(NSError *error)failureBlock>];
Here, I am using a StackMob method for asynchronously saving the MOC found in the NSManagedObjectContext(Concurrency) Category Reference.
The view before this one performs a fetch on recent posts, and in the case where the fetch is not performed posting works fine, but if a fetch was performed then in saving the MOC in order to upload the new post I receive the following output as an error message:
2013-09-11 17:08:09.284 imageTagging[1824:1843] -[__NSDictionaryI bytes]: unrecognized
selector sent to instance 0x1e3123d0
2013-09-11 17:08:09.291 imageTagging[1824:1843] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI bytes]: unrecognized
selector sent to instance 0x1e3123d0'
*** First throw call stack:
(0x318cb3e7 0x395c6963 0x318cef31 0x318cd64d 0x31825208 0x321631cf 0x3216b991 0x15ea99
0x318c8757 0x15e109 0x15dabf 0x10d1c3 0x318d05b7 0x10cd4d 0x10c829 0x10923b 0x1076d9
0x3166c431 0x316c44d1 0x1685c3 0x316c7e5d 0x399e3b3b 0x399e167d 0x399e4613 0x399e47d9
0x39a087f1 0x39a08684)
libc++abi.dylib: terminate called throwing an exception
(lldb)
The data is still uploading to the StackMob server, and can be called upon when the app is run later -- but the app crashes in trying to save it. All of this is performed in the view controller. I've tried to enforce all MOC saves to be performed on the main thread, but the error still occurs. I've also tried dispatching a "save queue" and updating the UI after save completes. This method seemed to work for a bit, but then the errors came up again (may have just been a fluke). I also tried to do this with the synchronous save calls in the documentation
The same error occurs when trying to perform other saves as well (such as after creating a new user or when updating a user's information), and all come down to the same function call causing the problems. It may also be worthwhile to note that the error is always the same (specifically that a type __NSDictionaryI is trying to access its unrecognized selector bytes.
Here is the full method call with the input parameters filled out:
//save context
[[[[SMClient defaultClient] coreDataStore] contextForCurrentThread] saveOnSuccess:^{
NSLog(#"You created a new Post object!");
[[[[SMClient defaultClient] coreDataStore] contextForCurrentThread] refreshObject:newPost mergeChanges:YES];
NSLog(#"refreshed");
} onFailure:^(NSError *error) {
NSLog(#"There was an error! %#", error);
}];
UPDATE: I have narrowed down the problem to a mishandling of information returned from a fetch performed by the previous view controller. Specifically, it occurs after the results are fetched in trying to use the data to update.
As a result of this new insight, the question I am really facing is how to properly save a managed object in the context after a fetch. I believe StackMob takes care of creating the managed objects after the fetch (i.e. server query). I've tried creating a new object from the results array (each "obj" is an NSManagedObject) with:
[results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSManagedObject *newObj = obj;
}];
I've also tried referencing the fetched results by object id (each "obj" is an objectID) with:
[results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSManagedObject *newObj = [self.managedObjectContext objectWithID:obj];
}];
Any insight on how to properly do this would be greatly appreciated!
UPDATE 2: It looks like the error is actually occurring in trying to use and save geolocation data. In order to use the queried objects' geolocation data, it must be unarchived -- but to save it, it must be archived. I'm looking into how to do this now, and if I come across a good solution, I'll update again.
FINAL UPDATE: Got it figured out! It turns out that the issue I was having was that I was unarchiving the geolocation data to update the UI and do some calculations, and while I was archiving it again to be stored properly, I created an annotation on a map that referenced the unarchived data. As a result, the MOC maintained the data that could not be saved via the StackMob methods. By only saving the archived data, I can save as often as I'd like and just unarchive the geodata when it needs to be used. Problem solved!
Please feel free to comment if anyone comes across a similar problem and needs some insight or references!
I am just going to put my final update in here as an answer, since it explains how I solved the issue.
FINAL UPDATE: Got it figured out! It turns out that the issue I was having was that I was unarchiving the geolocation data to update the UI and do some calculations, and while I was archiving it again to be stored properly, I created an annotation on a map that referenced the unarchived data. As a result, the MOC maintained the data that could not be saved via the StackMob methods. By only saving the archived data, I can save as often as I'd like and just unarchive the geodata when it needs to be used. Problem solved!
Please feel free to comment if anyone comes across a similar problem and needs some insight or references!
Moral of the story, if you are running into issues similar to this, make sure you are not (even if you don't mean to be) storing references to the unarchived SMGeoPoint data in any of your managed objects. It is trying to store those that causes the problem.

My app crashes if I change the control view repeatedly

I have an app that reads from a database some data ( ASIFormDataRequest ) and some images from server files ( setImageWithURL ). My app does it every time the viewController is changed (in order to have everything updated). So I guess there is an issue with cached images If I push my app to the limit switching viewControllers repeatedly my app crashes. Why is this?
All of this has appeared after adding the setImageWithURL functions. Has anyone run into something similar?
I may have to change the way my app works How do you guys do these calls to the server/SQL? How often?
EDITED:(NON_ANSWERED YET ):
XCODE does not say anything about it , when my device crashes it only displays:
2011-12-29 20:14:56.479 CaeDeCajon[4969:6e4f] arrayProductos.id :13
id_producto: 31 2011-12-29 20:14:56.481 CaeDeCajon[4969:7b5f]
arrayProductos.id :25 id_producto: 15 2011-12-29 20:14:56.490
CaeDeCajon[4969:7b5f] arrayProductos.id :31 id_producto: 15
2011-12-29 20:14:56.491 CaeDeCajon[4969:7b5f] arrayProductos.id :32
id_producto: 15 2011-12-29 20:14:56.395 CaeDeCajon[4969:955f]
arrayProductos.id :22 id_producto: 35 (gdb) // HERE IT STOPS
RUNNING.
for a better understanding of the question, my app is designed as follows:
Based on a 5-icon TabBar.
In the second icon I have a tableview with categories of products ( tables, chairs ... ) and if you press on one of them appears another viewController ( the usual detail view ) showing several products in a row ( thanks to a scrollView ) , here there is a navigationController with a button on it, when pressed it takes you to the gallery mode: showing the same products that were shown in the scrollView but in a gallery mode, if you press one of the products it takes you back to the scrollView and move your screen to the product chosen. Pretty normal stuff .
In the third icon I have the check-out basket, where every product picked up on the scrollView (where there is a buy button ) is pressed.
The case is that I "read" Asycn from the database all the information(no pictures) at launchingWithOptions and every time the viewController is changed, to make sure the user does not pick up a product that was sold out. I implanted this on its own and it seemed to worked fine and not to crash my app ( info : name, stock, ... only strings ). light data.
Here is where I think the problem is: I "read" all the images display from a file system in 1&1 ( hosting compony ), and seems to work fine and fast. The problem is when I swap/change the viewController repeatedly and quick between tableView-ScrollView-galleryMode , it crashed 4 times in a row for the same reason. I must say that I get the images for every viewController, for example the images in the scrollView,galleryMode and checkoutView are the same . Can I reuse them ? because I have calls to the URL everyViewController and I guess that is not healthy.
The code to download images:
NSString *URLphotos =[[NSString alloc]initWithFormat:#"http://www.myurl.com/imagenes/%#",picture1.jpg]; // this is not always picture1.jpg but I changed it for making it plainer.
[cell.photo setImageWithURL:[NSURL URLWithString:URLphotos]
placeholderImage:[UIImage imageNamed:#"placeHolder.png"]] ;
Is it enough? I got no more.
wanted tips:
How/where do you guys "read" the images from the URL in order not to crash the app but have the app updated all the time?
Is there something going on with my cache that is making me crazy ? fix it?
Thanks in advance for the interest
It's not clear if you are using Coredata but I had a similar experience with an app. It also had a tab bar and quickly pressing between items would cause random crashes. The issues was coredata being read / updated / deleted at the same time.
I resolved the issue by moving all of my coredata read, writes and updates to the main thread. (example in Swift):
dispatch_async(dispatch_get_main_queue(), {
myDatabase("NameHere", theCommand: "TRUNCATE", theQuery: "dummy")
})
I made a series of functions for the common coredata commands I needed so your code will of course be different. But try running coredata on the main thread and see if your problem is solved.
If you have a CPU intensive coredata task then you will should not run on the main thread but the complexity of the solution is increased (I have only been using Xcode for 3 months) and someone else may be able to help.
But if you are using coredata try this first to see if it solves your problem.

node.js process out of memory error

FATAL ERROR: CALL_AND_RETRY_2 Allocation Failed - process out of memory
I'm seeing this error and not quite sure where it's coming from. The project I'm working on has this basic workflow:
Receive XML post from another source
Parse the XML using xml2js
Extract the required information from the newly created JSON object and create a new object.
Send that object to connected clients (using socket.io)
Node Modules in use are:
xml2js
socket.io
choreographer
mysql
When I receive an XML packet the first thing I do is write it to a log.txt file in the event that something needs to be reviewed later. I first fs.readFile to get the current contents, then write the new contents + the old. The log.txt file was probably around 2400KB around last crash, but upon restarting the server it's working fine again so I don't believe this to be the issue.
I don't see a packet in the log right before the crash happened, so I'm not sure what's causing the crash... No new clients connected, no messages were being sent... nothing was being parsed.
Edit
Seeing as node is running constantly should I be using delete <object> after every object I'm using serves its purpose, such as var now = new Date() which I use to compare to things that happen in the past. Or, result object from step 3 after I've passed it to the callback?
Edit 2
I am keeping a master object in the event that a new client connects, they need to see past messages, objects are deleted though, they don't stay for the life of the server, just until their completed on client side. Currently, I'm doing something like this
function parsingFunction(callback) {
//Construct Object
callback(theConstructedObject);
}
parsingFunction(function (data) {
masterObject[someIdentifier] = data;
});
Edit 3
As another step for troubleshooting I dumped the process.memoryUsage().heapUsed right before the parser starts at the parser.on('end', function() {..}); and parsed several xml packets. The highest heap used was around 10-12 MB throughout the test, although during normal conditions the program rests at about 4-5 MB. I don't think this is particularly a deal breaker, but may help in finding the issue.
Perhaps you are accidentally closing on objects recursively. A crazy example:
function f() {
var shouldBeDeleted = function(x) { return x }
return function g() { return shouldBeDeleted(shouldBeDeleted) }
}
To find what is happening fire up node-inspector and set a break point just before the suspected out of memory error. Then click on "Closure" (below Scope Variables near the right border). Perhaps if you click around something will click and you realize what happens.

Resources