Deleting multiple CKRecords at the same time - objective-c

I have been programming in objective-C for about a year now, but i am new to cloud kit. I can do simple things such as fetch, save and delete Records but I have not been able to find a way of deleting multiple Records at a time.
I tried a for loop but, although there were no errors, nothing was deleted.
heres some of the code:
for (CKRecord* r in self.allRecords) {
[[[CKContainer defaultContainer] publicCloudDatabase] deleteRecordWithID:r.recordID completionHandler:^(CKRecordID *recordID, NSError *error) {
if (error) {
NSLog(#"error");
}else
NSLog(#"deleted");
}];
}
allRecords is an array containing the records which i need deleting but it does not delete any of the records.
Thanks

If you need to modify (that is, save or delete) multiple records in one CloudKit round-trip, you need to use a CKModifyRecordsOperation: https://developer.apple.com/library/prerelease/ios/documentation/CloudKit/Reference/CKModifyRecordsOperation_class/index.html
You mention "allRecords is an array containing the records which i need deleting but it does not delete any of the records".
It's not clear whether you mean that the records aren't being deleted from CloudKit, or you mean that the records aren't being deleted from your self.allRecords array.
In case you're expecting the records to be removed from self.allRecords: they won't. That's your job to manage after examining the response from either the CKModifyRecordsOperation or the deleteRecordWithID:completionHandler: call in your snippet above.

Related

Best way to delete Firestore document that you are unsure if it exists?

I'm using Firestore as the NoSQL database to build an app that needs to let users add as friends, block, ... other users
To block someone, I'll set the blocked values, and then delete the current friendship status (if any), but I find this a bit tricky. Should I first check if the document exists, and just then delete it, or does Firestore achieve this automatically? Would I be wasting time & Firestore operations if I add the extra checks?
fbRef.runBatch {
it.delete(userFriendsWith)
it.delete(blockedUserFriendsWith)
...
}
fbRef.runBatch {
it.get() {
...
if (document.exists()) {
it.delete(userFriendsWith)
}
}
}
Thanks!
Just delete the document. There's no need to read it first, if you don't care what's inside. The delete operation won't fail if the document already doesn't exist.

Symfony2, Doctrine, add\insert\update best solution for big count of queries

Let's imagine we have this code:
while (true)
{
foreach($array as $row)
{
$item = $em->getRepository('reponame')->findOneBy(array('filter'));
if (!$item)
{
$needPersist = true;
$item = new Item();
}
$item->setItemName()
// and so on ...
if ($needPersist)
{
$em->persist();
}
}
$em->flush();
}
So, the point is that code will be executed a lot of times (while server won't die :) ). And we want to optimize it. Every time we:
Select already entry from repository.
If entry not exists, create it.
Set new (update) vars to it.
Apply actions (flush).
So question is - how to avoid unnecessary queries and optimize "check if entry is exist"? Because when there are 100-500 queries it's not so scary... But when it comes up to 1000-10000 for one while loop - it's too much.
PS: Each entry in DB is unique by several columns (not only by ID).
Instead of fetching results one-by-one, load all results with one query.
Eg.
let's say your filter wants to load ids 1, 2, 10. So QB would be something like:
$allResults = ...
->where("o.id IN (:ids)")->setParameter("ids", $ids)
->getQuery()
->getResults() ;
"foreach" of these results, do your job of updating them and flushing
While doing that loop, save ids of those fetched objects in new array
Compare that array with original one using array_diff. Now you have ids that were not fetched the first time
Rinse and repeat :)
And don't forget $em->clear() to free memory
While this can still be slow when working with 10.000 records (dunno, never tested), it will be much faster to have 2 big queries than 10.000 small ones.
Regardless if you need them to persist or not after the update, retrieving 10k+ and up entries from the database and hydrating them to php objects is going to need too much memory. In such cases you should better fallback to the Doctrine DBAL Layer and fire pure SQL queries.

Core Data, duplicate and deleted RSS entries

I've been working on an RSS Reader, using core data for caching. Like a lot of people, I wanted to avoid duplicate entries, which led me to this question, and also this one.
But, there was another thing I wanted, I also wanted to give users the ability to delete articles, and avoid adding deleted articles again when refreshing a feed, that is if the deleted article still existed in the feed. So, my solution currently, is, to maintain another entity in my managed object context with unique identifiers (which how I identify each item in the feed) of deleted articles, I just add the identifier of the article that is being deleted to that entity, and check against it.
Now, here is a piece of code that I wrote to accomplish all of the above. This code is run every time a new item in the feed is parsed, during the parsing process.
dispatch_queue_t checkQueue = dispatch_queue_create("com.feedreader.backgroundchecking", NULL);
dispatch_async(checkQueue,^{
NSMutableArray *mutablesortedArticles = [NSMutableArray arrayWithArray:self.feeds.sortedArticles];
if (!mutablesortedArticles) {
// Handle the error.
}
if ([[mutablesortedArticles valueForKey:#"identifier"]
containsObject:article.identifier]) {
NSLog(#"This article already exists");
return;
}else {
NSMutableArray *mutabledeletedArticles = [NSArray arrayWithArray:self.alldeletedArticles];
if (!mutabledeletedArticles) {
// Handle the error.
}
if ([mutabledeletedArticles valueForKey:#"identifier"]
containsObject:article.identifier]) {
NSLog(#"This article has been deleted");
return;
}else {
Article *newArticle = [NSEntityDescription insertNewObjectForEntityForName:#"Article" inManagedObjectContext:self.managedObjectContext];
newArticle.title = article.title;
newArticle.date = article.date;
newArticle.link = article.link;
newArticle.summary = article.summary;
newArticle.image = article.image;
newArticle.identifier = article.identifier;
newArticle.updated = article.updated;
newArticle.content = article.content;
newArticle.feed = self.feed;
dispatch_async(dispatch_get_main_queue(),^{
NSError *error = nil;
[self.managedObjectContext save:&error];
if (error) {
NSLog(#"%#", error);
}
});
}
}
});
Both, self.feeds.sortedArticles and self.alldeletedArticles are fetched from managed object context before parsing starts.
My problem begins when this code is being run, the UI freezes, for 1-2 seconds (I tried it with a feed that had a little more than 500 articles in the managed object context). So, I guess my question is, is there a more efficient way to do what I'm trying to do here, one that hopefully doesn't freeze the UI?
Perhaps a better way of dealing with deleted articles?
My first suggestion would be to handle deleted articles by added a "itemDeleted" property to the Article entity. Then you have only one list of objects to check when inserting new items.
(Hint: Don't call that attribute "deleted". isDeleted is a built-in property of NSManagedObject, so that is likely to cause name collisions.)
The next suggestion is to save the managed object context only after all items have been imported, and not after each each item (EDIT: See also Caffeine's answer, which was posted while I was writing this.)
Finally, searching each new item in the list of all articles separately is a pattern that does not scale well. Implementing Find-or-Create Efficiently in the "Core Data Programming Guide" describes a pattern that might be better:
for a list of to-be-inserted items, perform a fetch request that fetches all items of this list which are already present in the database,
traverse both the list of new items and the fetched list in parallel, to find out which items are really new and have to be inserted.
The UI freeze is probably caused by [self.managedObjectContext save:&error] since writing out all the objects to disk takes a couple seconds. A great solution to this in iOS 5+ are nested contexts. See this blog post for more details http://www.cocoanetics.com/2012/07/multi-context-coredata/ in particular the Asynchronous Saving section at the end.

WCF Data Service - update a record instead of inserting it

I'm developing a WCF Data Service with self tracking entities and I want to prevent clients from inserting duplicated content. Whenever they POST data without providing a value for the data key, I have to execute some logic to determine whether that data is already present inside my database or not. I've written a Change interceptor like this:
[ChangeInterceptor("MyEntity")]
public void OnChangeEntity(MyEntity item, UpdateOperations operations){
if (operations == UpdateOperations.Add)
{
// Here I search the database to see if a matching record exists.
// If a record is found, I'd like to use its ID and basically change an insertion
// into an update.
item.EntityID = existingEntityID;
item.MarkAsModified();
}
}
However, this is not working. The existingEntityID is ignored and, as a result, the record is always inserted, never updated. Is it even possible to do? Thanks in advance.
Hooray! I managed to do it.
item.EntityID = existingEntityID;
this.CurrentDataSource.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
I had to change the object state elsewhere, ie. by calling .ChangeObjectState of the ObjectStateManager, which is a property of the underlying EntityContext. I was mislead by the .MarkAsModified() method which, at this point, I'm not sure what it does.

Filtering NSFetchedResultsController results at runtime

What's the best way to implement runtime filtering of Core Data objects using NSFetchedResultsController?
For example, I want to be able to display all Record-entities in a RecordStore-entity, but also filter all Records in a RecordStore for some predefined critera, eg (ANY recordStore.records.count > 0).
I read that changing an NSFetchedResultsController's predicate after it has been created is bad. So should I store the fetched results in an NSArray that I can filter and use that as the UITableView's datasource, or should I create multiple NSFetchedResultsControllers?
You can re-fetch the data when you need to update. If the data itself changed, then you can just call fetch again. If your criteria changed, then set the predicate on your NSFetchedResultsController, and call fetch.
Create the NSFetchRequest instance, update request each time and perform fetch. Delete cache if you are using:
let shortDescriptor = NSSortDescriptor(key: key, ascending: ascending)
fetchedResultViewController.fetchRequest.sortDescriptors = [shortDescriptor]
NSFetchedResultsController<NSFetchRequestResult>.deleteCache(withName: fetchedResultViewController.cacheName)
do {
try fetchedResultViewController?.performFetch()
} catch let error as NSError {
print("Error in fetch \(error)")
}
Read the apple document: https://developer.apple.com/reference/coredata/nsfetchedresultscontroller