How can I communicate between two Today widgets in my app? - ios

have two today widgets.
on viewDidLoad for both of them I fetch the latest data and save to core data like this:
CloudAssistant.shared.fetchServicesForCurrentMonth { _ in
self.updateView()
}
And here is the problem. If widgets are loaded, the same time I perform fetch from CloudKit. Records are parsed and saved to core data simultanously. So... it leads to doubled records for my Service entity.
I do not know how to solve this. Is there a way to do this?

Related

SwiftUI - How can I know when my FetchedResults changes?

I'd like to pass to my model the newest fetched data of my core data entities, in order to have them synched.
Is this possible?
The reason is that I have many variables that have to be calculated from the data saved in core data. These values are used in my views, so they should update at the same time.
(Until now I just found a way to pass them around every time with functions, but I find this very chaotic...)
Until now:
func doSomethingWithFetchedData(fetchedData: FetchedResults<Entity>) {
//return what I need
}
Thanks!
NSFetchedResultsController Subscribing to updates for many objects matching a fetch request has been easier than subscribing to updates from a single managed object, thanks to NSFetchedResultsController. It comes with a delegate that informs us about changes to the underlying data in a structured way, because it was designed to integrate with tables and collection views
Here is a good link to start with

Multi-threading with core data and API requests

Intro
I've read alot of tutorials and articles on Core Data concurrency, but I'm having an issue that is not often covered, or covered in a real-world way that I am hoping someone can help with. I've checked the related questions in SO and none give an answer to this particular question that I can find.
Background
We have an existing application which fetches data from an API (in the background thread) and then saves the records returned into core data. We also need to display these records in the application at the time.
So the process we currently go through is to:
Make a network request for data (background)
Parse the data and map the objects to NSManagedObjects and save (background)
In the completion handler (main thread) we fetch records from core data with the same order and limit that we requested from the API.
Most tutorials on core data concurrency follow this pattern of saving in one thread and then fetching in another, but most of them give examples like:
NSArray *listOfPeople = ...;
[NSManagedObjectHelper saveDataInBackgroundWithContext:^(NSManagedObjectContext *localContext){
for (NSDictionary *personInfo in listOfPeople)
{
PersonEntity *person = [PersonEntity createInContext:localContext];
[person setValuesForKeysWithDictionary:personInfo];
}
} completion:^{
self.people = [PersonEntity findAll];
}];
Source
So regardless of the amount of records you get back, you just fetch all content. This works for small datasets, but I want to be more efficient. I've read many times not to read/write data across threads, so fetching afterwards gets around this issue, but I don't want to fetch all, I just want the new records.
My Problem
So, for my real world example. I want to make a request to my API for the latest information (maybe anything older than my oldest record in core data) and save it, them I need the exact data returned from the API in the main thread ready for display.
So my question is, When I reach my completion handler, how do I know what to fetch? or what did the API return?. A couple of methods I've considered so far:
after saving each record, store the ID in a temporary array and then perform some fetch where id IN array_of_ids.
If I am asking for the latest records, I could just use the count of records returned, then use an order by and limit in my request to the latest x records.
My Question
I realize that the above could be answering my own question but I want to know if there is a better way, or is one of those methods much better to use than the other? I just have this feeling that I am missing something
Thanks
EDIT:
Neither answer below actually addresses the question, This is to do with fetching and saving data in the background and then using the returned data in the main thread. I know it's not a good idea to pass data between threads, so the common way around this is to fetch from core data after inserting. I want to work out the more efficient way.
Have you checked NSFetchedResultsController? Instead of fetching presented objects into array, you will use fetched controller in similar fashion. Through NSFetchedResultsControllerDelegate you would be notified about all the changes performed in background (rows added, removed, changed) and no manual tracking would be needed.
I feel You missing case with two silmultaneous API calls. Both storring ids and counting created enities wont work for that case. Consider adding timestamp property for each PersonEntity.
Assuming that Your intention is to display recently updated persons.
The calcutation of the oldest timestamp to display can look like this:
#property NSDate *lastViewRefreshTime;
#property NSDate *oldestEntityToDisplay;
(...)
if (self.lastViewRefreshTime.timeIntervalSinceNow < -3) {
self.oldestEntityToDisplay = self.lastViewRefreshTime;
}
self.lastViewRefreshTime = [NSDate date];
[self displayPersonsAddedAfter: self.oldestEntityToDisplay];
Now, if two API responses returns in period shorter than 3s their data will be displayed together.

Comparing to Server API for Synching

I need to synch up Core Data on phone with MYSQL database on server. I have gotten as far as capturing JSON feed in iOS in array. My question is, what is the best way to compare and sync up the items?
I know there are many SO posts and tutorials on syncing however the comparison is often brushed over. Once you have the JSON feed in an array and also have a managed object context, how do you compare one entry against the other?
My strategy is to give each locally saved object (record) a local id. Then when syncing occurs if the local object (record) has no server id, add it to the server table. However, I'm not sure how that works with the managed object context.
Do you iterate through the local records and compare them against server records?
Or do you iterate through the JSON array against the local managed object context?
for (NSDictionary *item in names) {
id serverid = [item objectForKey:#"serverid"];
// .... check against something in managed objectcontext
if (!items.serverid ) {
///insert on server and also provide obtained serverid to managedobjectcontext
}
}
Would appreciate any insights or suggestions.
Tim Isted addressed a similar issue in his exceptional talk at WWDC 2013.
You should watch the whole thing, because it's pretty awesome. However, the relevant part for this topic starts about 12:43 into the presentation.

UITableView handling Json and Core Data

What would be the best practise and best for user experience to achieve the following?
1:) Retrieve data from JSON
2:) Store in Core Data
3:) Display in UITableViewController
Do i store the JSON first, then populate the table using the stored data? OR Do i store it in the Core Data (background process) and populate the table using the JSON for the first time?
I want the user to be presented with a UITableview with minimum load time.
Thanks
This is what I would do:
Create your Core Data database and model.
Create a data access layer that will contain the read and write methods for each of your objects
In the read functions you can query the core data, if there is data then return that. Then in the background call the web server and and update your core data with the new JSON.
If there is no data go and request it from the web server, populate your core data tables using the JSON and then return the data from the core data so it is always consistent.
You can also have a last update date on your data so you are only requesting the new data from the web server that isnt already in your local core data DB. This will reduce the amount of data coming down to your ios device.
If you want minimum load time then I'd serve from JSON and that save to CoreData afterwards. That way the user can see content straight away without first having to wait for all the data to be saved (and parsed).
The course of action in this matter heavily depends on:
A. The amount of JSON data you are downloading
B. How effective your backend is at only sending necessary JSON results (rather than sending everything in bulk)
C. How you are attaching Core Data to your UITableViewController.
I recently made a pretty big project that does exactly this, which involved fetching a pretty big chunk of JSON, parsing it, and inserting it into Core Data. The only time there is any delay is during the initial load. This is how I accomplished it:
Download JSON. Cast as [[String: AnyObject]]: if let result = rawJSON as? [[String: AnyObject]] {}
Check to see if IDs for objects already exist in Core Data. If that object already exists, check if it needs update. If it doesn't exist, create it. Also check if IDs have been deleted from the JSON, if so remove them from.
Use NSFetchedResultsController to manage data from Core Data and populate the UITableView. I use NSFetchedResultsController rather than managedObjectContext.executeFetchRequest() because NSFetchedResultsController has delegate methods that are called every time the managedObjectContext is updated.

How to sync data from web service with Core Data?

I'm trying to sync my data from a web service in a simple way. I download my data using AFNetworking, and using a unique identifier on each object, I want to either insert, delete or update that data.
The problem is that with Core Data you have to actually insert objects in the NSObjectManagedContext to instantiate NSManagedObjects. Like this:
MyModel *model = (MyModel *)[NSEntityDescription insertNewObjectForEntityForName:#"MyModel" inManagedObjectContext:moc];
model.value = [jsonDict objectForKey:#"value"];
So when I get the data from the web service, I insert them right away in Core Data. So there's no real syncing going on: I just delete everything beforehand and then insert what's being returned from my web service.
I guess there's a better way of doing this, but I don't know how. Any help?
You are running into the classic insert/update/delete paradigm.
The answer is, it depends. If you get a chunk of json data then you can use KVC to extract the unique ids from that chunk and do a fetch against your context to find out what exists already. From there it is a simple loop over the chunk of data, inserting and updating as appropriate.
If you do not get the data in a nice chunk like that then you will probably need to do a fetch for each record to determine if it is an insert or update. That is far more expensive and should be avoided. Batch fetching before hand is recommended.
Deleting is just about as expensive as fetching/updating since you need to fetch the objects to delete them anyway so you might as well handle updating properly instead.
Update
Yes there is an efficient way of building the dictionary out of the Core Data objects. Once you get your array of existing objects back from Core Data, you can turn it into a dictionary with:
NSArray *array = ...; //Results from Core Data fetch
NSDictionary *objectMap = [NSDictionary dictionaryWithObjects:array forKeys:[array valueForKey:#"identifier"]];
This assumes that you have an attribute called identifier in your Core Data entity. Change the name as appropriate.
With that one line of code you now have all of your existing objects in a NSDictionary that you can then look up against as you walk the JSON.
The easiest thing to do is to restore the Json to a entity that maps properly to it. Once you've mapped it, determine if a object matching the entities ID exists already, if so then fetch the entity and merge changes. If not, create a new entity in Core Data and restore the Json to it.
I'm building a app were I do client side syncing with Evernote. They keep a syncUpdate number on all of their objects and at the server level. So when I start my sync I check if my clients syncUpdate count is less than the servers. If so, I know I am out of sync. If my updateCount is at 400 and the server is at 410, I tell the server to provide me with all objects between updateCount 400 and 410. Then I check if I already have the objects or not and perform my update/create.
Every time a object is modified on the server, that objects updateCount is increments along with the servers.
The server also keeps a time stamp of the last update, which I can check against also.

Resources