I need to run a SYNCHRONOUS call to parse.com. This is what I got:
var query = PFQuery(className:"myClass")
query.whereKey("groupClassId", equalTo:self.currentGroupId)
query.selectKeys(["objectId", "firstName", "lastName"])
self.arrayCurrentData = query.findObjects() as Array<myData>
This return the correct number of rows from parse.com and fills up my local array. But how can I extract the data from the array? If I look at the array at runtime it shows that all the data I need is in 'serverData' in self.arrayCurrentData.
Normally if I loop an async(findObjectsInBackgroundWithBlock) filled array I would ask
self.arrayCurrentData[i].lastName
to get the lastName, but that is not the case in the sync array. There I can't ask directly for values (or so it seems).
Anyone who know what I am talking about and how to get data synchronous from parse.com?
Get the PFObject's attributes with valueForKey(). This is true whether or not the object was fetched synchronously. In other words...
self.arrayCurrentData[i].valueForKey("lastName")
EDIT - This approach generates a compiler message because you've typed the response as Array<myData>. But find returns PFObjects, so ...
self.arrayCurrentData = query.findObjects() as [PFObject]
... is the correct cast. I'm not a swift speaker, but the expression self.arrayCurrentData[i].lastName pleases the compiler because arrayCurrentData[i] is typed as myData. But this fails at run time because the real returned objects are PFObjects.
As an aside, I'd take a hard look at the rationale for fetching synchronously. I can't think of a case where its a good idea on the main thread. (off the main okay, but then you've already opted for asynch vs. the main, and the block-based methods provide a good way to encapsulate the post-fetch logic).
Related
There's a API callback returns a Json format result. The requirement in short is that I need to keep calling this API and keeps implement Breadth First Search on the results it returns.
Imaging it's a map with many nodes and connections. Every time I call API call for a node, it gonna return me list of its connected nodes. All I need now, is an array, which saves all the node that has been visited to avoid repeated visits.
But this is Swift and I am new to it. I was using Array and pass as inout inside the completion handler. But there's an error: escaping closures can only capture inout parameters explicitly by value which means I cannot do it like this.
Now you may ask why the array I have has to be stored by reference. Because the API call is async, which means I have to wait until it comes back to keep progressing Breadth First Search, means I have to pass this array by reference in order to do the recursion.
What other solutions I may have?
Swift Arrays are value types (not reference types) so you will need to store you array in an object. You can then pass the object to your handler and set the array content inside the object which is carried as a reference in the closures.
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.
I'm creating two PFObjects at the same time that should reference each other's object IDs when they're saved. In the example below, the second object is supposed to save the first object's object ID in an array.
let objectForFirstClass = PFObject(className:"ClassOne")
let objectForSecondClass = PFObject(className: "ClassTwo")
objectForSecondClass.setObject([objectForFirstClass.objectId!], forKey: "classOneObjectArray")
The last line is causing the error because objectForFirstClass.objectId is nil. I'd assume this is because the object hasn't been saved yet. How can I fix this?
You want to save after creating the first object, and in the completion handler, create the second one with a reference to the first one.
You can use saveAllInBackground:block: for this.
Correct, the object id is assigned by the server when saved. I'd be tempted to write some cloud code to do what you want so you can send some details and the cloud code will create and connect the objects, then return both of them to you. You can of course do the same thing locally in your app, there's just more network comms.
You should also consider using pointers or relationships. These are better for querying, though the same save requirements apply before you can set the connections.
Based on some limited testing, I see that if I
Execute a Fetch request with result type = NSDictionaryResultType
Do some manipulations on the returned values
Store back the MOC on which Fetch request was executed
the changes in step 2 are not written back to the persistent store because I am changing a dictionary and not a "managed object". Is that a correct understanding?
Most likely you are abusing the dictionary result type. Unlike in conventional database programming, you are not wasting valuable memory resources when fetching the entire objects rather than just one selected attributes, due to an under-the-hood mechanism called "faulting".
Try fetching with managed object result type (default) and you can very easily manipulate your objects and save them back to Core Data. You would not need to do an additional fetch just to get the object you want to change.
Consider dictionaries only in special situations with huge data volumes, difficult relational grouping logic, etc., which make it absolutely necessary.
(That being said, it is unlikely that it is ever absolutely necessary. I have yet to encounter a case where the necessity of dictionaries for fetches was not an indirect result of flawed data model design.)
Yes, kind of, you can't store a dictionary back into the context directly so you can't save any updates that way.
If you get a dictionary object then you need to include in it the associated managed object id (if it isn't aggregated) or do another fetch to get the object(s) to update.
I have a parse class(named "Story") with following columns:
contents - Array of Pointers to Media class
creator - User pointer
lastPosted - Date
title - String
users - array of pointers to User class
I am trying to update a PFObject from "Story" class like this
storyObject.addUniqueObjectsFromArray(selectedFriends, forKey: "users")
storyObject.setObject(createStoryTextField.text, forKey: "title")
storyObject.saveInBackgroundWithBlock({ (succeed: Bool, error: NSError?) -> Void in
hud.hide(true)
})
In some cases the completion block takes a lot of time to complete. By debugging, I have found that the more objects in the "contents" column, the longer it takes. But I haven't even updated the "contents". All the values in the code are not nil. I checked for that too. Any help would be appreciated. Thanks.
This is potentially expected behavior. In general when you put something on a background thread (like saveInBackgroundWithBlock is almost definitely doing) it will take a lot longer to complete than if you call it on the main thread. If you need it to return quickly and don't mind blocking the UI, you can use the save: method.
You also may want to check to see how many requests you are making simultaneously as this may slow down each one.
Finally, if you want to customize the network behavior of Parse more than you already do, you can just use the REST API and make all your own network calls (using NSURLSession or a third party library like AFNetworking.)