Init object with asynchronously downloaded data - ios

I have an object that needs to be initialised with data from network and doesn't really make sense without the downloaded data. But it seems to me that doing an asynchronous network call in its init method is not a good idea because the object will not be ready to user right away and it might cause confusion. Should I only use a basic init method that will alloc init all its properties to create an empty object, and have other (non-init) methods populate the data from the network which will be called explicitly by other objects (such as the view controller using this object)? What would be a good way to approach this?

I think that the solution comes from having the right order of code running:
1) Go to network and fetch data (show the user acivity indicator in this time)
2) Server return response -> fetch the response into your object
3) Show the data or use it

Related

Why does storing a reference to an NSManagedObject prevent it from updating?

This question is poorly phased but this can be better explained in code.
We have a Core Data Stack with private and main contexts as defined by Marcus Zarra here: http://martiancraft.com/blog/2015/03/core-data-stack/
We call a separate class to do a fetch request (main context) and return an array of NSManagedObjects:
NSArray *ourManagedObjects = [[Client sharedClient].coreDataManager fetchArrayForClass:[OurObject class] sortKey:#"name" ascending:YES];
We then do some processing and store a reference:
self.ourObjects = processedManagedObjects
Our view contains a UITableView and this data is used to populate it and that works just fine.
We change the data on our CMS, pull to refresh on the UITableView to trigger a sync (private context) and then call this same function to retrieve the updated data. However, the fetch request returns the exact same data as before even though when I check the sqlite db directly it contains the new data. To get the new values to display I have to reload the app.
I have discovered that if I don't assign the processedManagedObjects to self, the fetch request does indeed return the correct data, so it looks like holding a reference to the NSManagedObject stops it from getting new data from the main context. However I have no idea why that would be.
To clarify, we're pretty sure there's nothing wrong with our Core Data Stack, even when these managed objects are not being updated, other are being updated just fine, it's only this one where we store a local reference.
It sounds like what's going on is:
Managed objects don't automatically update themselves to reflect the latest data in the persistent store when changes are made via a different managed object context.
As a result, if you keep a reference to the objects, they keep whatever data they already had.
On the other hand if you don't keep a reference but instead re-fetch them, you get the new data because there was no managed object hanging around with its old data.
You have a few options:
You could keep the reference and have your context refresh the managed objects, using either the refresh(_, mergeChanges:) method or refreshAllObjects().
If it makes sense for your app, use an NSFetchedResultsController and use its delegate methods to be notified of changes.
Don't keep the reference.
The first is probably best-- refreshAllObjects() is probably what you want. Other options might be better based on other details of your app.
Try setting the shouldRefreshRefetchedObjects property of the fetch request to true. According to the documentation:
By default when you fetch objects, they maintain their current property values, even if the values in the persistent store have changed. Invoking this method with the parameter true means that when the fetch is executed, the property values of fetched objects are updated with the current values in the persistent store.

Should I use operation queue for this complete scenario?

I need to perform a scenario with the following steps:
To make a network call with some search parameters provided by the user
Then, to parse its JSON response and create model entities
Then, for each entity created and if it has an associated image URL, to call the corresponding service for downloading such image and to show it in a UICollectionView when download finishes.
I need to handle concurrent network calls when downloading the N images I'd need. User should have the possibility to cancel the current search flow and start a new one. In addition, I should take into account that the cell for a certain image has not been displayed yet or it has been dequeued. So, my question is: which would be the best approach for this?
Perform the first network call (no concurrency) and the JSON response parsing directly using URLSession and URLSessionDownloadTask, and using Operation and OperationQueue for the subsequent steps. Use both invalidateAndCancel() for the URLSession and cancelAllOperations() for the OperationQueue if the user wants to cancel the current search.
Perform the complete process using Operation and OperationQueue.
Any other and more appropriate approach?
The best approach would be to do either a sync or async call to make the first service call. Once completed, parse the json synchronously and then use lazy loading concept (async call) to load if any images are to be downloaded to display the images.
Perform the network call then make the model objects. return these to your VC through your API completion block.
Then reload your collection view and pass the url to each cell and have each cell worry about itself, use something like SDWebImage to show the images async in the cells..

When to use a predicate over passing a CoreData object via the prepare method

I have an iOS app that I am currently building. I'm using CoreData for the first time and have a question in regards to passing data from one ViewController to the next. Right now I have a tableview set up in VCa, and when I tap a cell I want to get the details of that object (Person) in VCb. I have been thinking of doing this in 2 different ways.
Pass a reference of the Persons name in the prepage function. And over in VCb use a predicate to fetch that person back out of CoreData. Is using another fetch request the correct solution here. There will not be a ton of data in this app, so I don't think it would be very taxing on the system to do another request, but I'm new to CoreData.
Capture the data object in VCa and pass that object over to VCb via the prepare function. This would eliminate me having to do another fetch request in VCb but seems like I could be limiting myself for future expansions.
Again, I'm new to CoreData and just wanted some thoughts on which method of thinking is better and why.
Don't ever fetch an object that you have already fetched. In your case #1 you already have the object that you need, but you'd go ahead and fetch it again for no good reason. It might not be "very taxing" in your case but that just means you're probably not duplicating a lot of work for no reason yet, but it's still a bad design. And anyway-- this is an entity representing a person. Are you certain that there would never, ever be more than one person with the same name?
Your second idea is the better one here.

Scrolling tableview while updating its datasource fetched from CoreData crash

Here is my context:
When I launch my app I fetch local data from CoreData and fill a tableview with it. At the same time I send an asynchronous request to my webservice to fetch what will be the new content of my tableview.
As soon as the request sends me a response I delete all the instances of the current NSManagedObjects and create new ones with the new data I got. Then I replace the datasource of my tableview to an array of the new NSManagedObjectContexts instances.
My problem:
I'm getting an error : CoreData could not fulfill a fault for ... if I scroll my tableview when the request finished and is triggering the deletion/creation of my tableview's data source.
I understand that this problem occurs because I'm trying to access an old NSManagedObject instance while doesn't exist anymore as it is explained in the doc : Apple doc. But I have no idea of what are the best practices in my case.
I don't want to block the user until my request finished but I have to prevent any error if he accesses "old" data while the request didn't finish (e.g : what if the user taps on a cell and I pass an instance of an NSManagedObject to another viewcontroller but when the request finishes this object doesn't exist anymore ?)
I would appreciate any help !
I highly recommend you to use NSFetchedResultsController since it's sole purpuse is:
You use a fetched results controller to efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object.
When using a fetched results controller it is much easier to handle the Core Data events like insert, delete, update.
You say you have three sections in your table view? That's no problem, NSFetchedResultsController can handle all of that.
Take a look at this. Apple provides a very nice set of instructions on how to configure and use NSFetchedResultsController.

How to avoid calling a method multiple times when the method takes long time to complete

There are several view controllers in my app where I need to sync the local contents with server using a method running in a background thread. Sometimes I need to insert data to my database on server if user has created any. The approach I am using here is to set a flag(something like isSynced = NO) on objects that I need to sync with server (there objects are in Core Data). When the syncing is complete my method will get rid of the flag(e.g. isSynced = YES) so it won't be sent again next time.
Now the problems is that the syncing method takes very long to complete(1 or 2seconds.). If now user pops out this particular view controller and swiftly comes back the previous call is still in progress and next one will be kicked off. The consequence is that there might be duplication in database.
My approach now is the make the syncing method to be called by a Singleton object:
#property (nonatomic) BOOL isSyncing;
//every time before syncing. check if object is available for syncing
if (!isSyncing) {
isSyncing = YES;
// sync server
// when complete
isSyncing = NO;
// post notification to view controller to reload table
} else {
// cancel because previous call is not finished
}
My concern is that if the call is cancelled my view controller will not be able to receive the notification is waiting for. I can fix this by posting another notification in the event of cancelation. I am wondering if this is the right to do this because I think that this problem should be pretty common in iOS development and there should be a standard way to deal with it
Your singleton approach may not be necessary. I don't see the harm in sending a database insert for each new object. You will still need to ensure each object is synched. That is, update the "isSynched" flag. Keep each object that needs to be synced in a "need to synch" list.
Then, update the "isSynced" flag by performing a background query on the database to check if the object exits. Then, use the result of the query to set the isSynched flag.
If the query result indicates the object is not in the database you then resend the object and leave it's "isSynced" flag set to NO.
If the query result indicates the object is in the database, set the "isSynced" flag to YES and remove it from your "need to synch" list.
An approach for preventing duplicate database entries is to make a unique key. For example, tag each with a hash based on the time and date. Then configure the table to ensure each key is unique.

Resources