When I implement the getLoggedInUserOnSuccess:onFailure method (or the loginWithUsername: password: onSuccess:^(NSDictionary *results)...method in xcode, the results array does not become available until after all of my code has finished running. (If I NSLog the results, they will show up correctly.)
There is one other question that mentions this in Stackoverflow:
How to get the sm_owner User object using StackMob as backend
However, the Stackmob Evangelist in the answer here does not suggest that there is any requirement for a completion block or something of this nature. (And in fact, in his own code, it appears to work without such a block or any sort of "waiting.") This was my first hunch as to what might be going wrong.
(Without posting a ton of code, I am trying to use this function to get the sm_owner which then serves as the predicate in the FetchedResultsController's getter, to ensure the user only sees their own creations and not those of other users, when in one view; in another fetch, they might be able to see the creations of users they follow.)
Has anyone else tried to use one of these methods with a results dictionary returned to write the predicate on a FetchedResultsController or similar and been able to make it work?
None of the Stackmob tutorials appear to limit data returned from a database based on its creator as far as I can tell.
If you set your schema's read permissions to "Allow to sm_owner", then you don't need to place a predicate on the fetch. Doing a generic fetch will return only those objects owned by the current logged in user.
Related
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 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.)
I am new to Parse cloudcode and spinning my wheels to understand JS and write cloudCode to remove user from PFRelation. Can anyone please assist me with parse cloudcode snippet to remove user from PFRelation. I am trying to implement unfriend functionality in the iOS app and I can remove the friend from current users PFRelation and would like to remove current user from the friend PFRelation. I am completely blanked out and don't know how to do that.
I appreciate the help.
Thanks!
The javascript info (that's what you need to do this function on the server) is here: https://parse.com/docs/js_guide#objects-pointers
Specifically, the code on that page that you need is:
var relation = request.user.relation("friends");
relation.remove(other);
request.user.save();
Then you need to get a handle to the other user object and do the same thing there. Are you storing Parse.User objects, or IDs? If it's User objects, the whole code could be this:
var relation = request.user.relation("friends");
var other = relation[0]; // I'm not sure about array indexing though.
var otherRelation = other.relation("friends");
otherRelation.remove(user);
relation.remove(other);
other.save();
request.user.save();
Note that this code isn't really kosher from the perspective of "when the function returns, the logic is done". It's not, since the save is asynchronous. It's faster this way. You could make it both fast and kosher by running both together and waiting for both to finish, but it's been a long time since I've written JavaScript so I can't provide the exact code for that.
Edit: Don't forget to use Parse.Cloud.useMasterKey() to get the right permissions before you try modifying another user.
I need a help in determining the most appropriate and convenient way to implement caching in my app, so that it would provide the behavior described below.
The app needs to display a list of objects A. It displays objects from cache, while simultaneously requesting the updated version of the list from server. If request is successful, cached objects are completely replaced by the returned ones. The list presented to the user is then refreshed.
Right now I'm achieving this behavior by having a dedicated singleton of a class called DataManager, which incapsulates one instance of ApiClient (that sends requests to server) and one instance of CacheProvider (which is basically a set of convenient methods for accessing Core Data objects). Each method in DataManager looks something like this:
- (NSArray *)objectsAWithUpdatedBlock:(void (^)(NSArray *updatedObjects))updatedBlock
{
[_apiClient requestObjectsAWithCompletionBlock:^(NSArray *objects, NSError *error) {
if (error == nil) {
[_cacheProvider replaceCachedObjectsAWithObjects:objects];
updatedBlock([_cacheProvider cachedObjectsA]);
}
}];
return [_cacheProvider cachedObjectsA];
}
But I have a feeling that I am reinventing the wheel and there must be some standard ways to get the same behavior with RestKit. Am I right or should I continue using my approach?
I'd use an NSFetchedResultsController to get the results to display. It doesn't care if they're cached or not, it just uses what is in the data store and observes changes there (so you don't need your own callback blocks).
RestKit offers a feature called fetch request blocks, which isn't a great name necessarily, but you use it to delete orphaned objects from the data store - i.e. all of your old cached objects.
Using these 2 approaches will likely massively reduce the amount of custom code you need to write.
Similar to the iPhone Facebook app search function, I am implementing search as you type functionality into my application although I have a problem when decoding the data into JSON format.
Basically what happens is because some searches take longer than others, they return at different intervals and this causes some small visual issues when the data is presenting on the screen.
I have set an NSLOG after each decode using NSJSONSerialization for the keyword 'industry'
2013-04-09 23:38:18.941 Project Name [42836:1d03] http://fooWebAddress/json/?method=search&limit=10&q=indus
2013-04-09 23:38:19.776 Project Name [42836:3e07] http://fooWebAddress/json/?method=search&limit=10&q=indu
2013-04-09 23:38:20.352 Project Name [42836:8803] http://fooWebAddress/json/?method=search&limit=10&q=indust
2013-04-09 23:38:21.814 Project Name [42836:4e03] http://fooWebAddress/json/?method=search&limit=10&q=industr
2013-04-09 23:38:23.434 Project Name [42836:8803] http://fooWebAddress/json/?method=search&limit=10&q=ind
2013-04-09 23:38:24.070 Project Name [42836:7503] http://fooWebAddress/json/?method=search&limit=10&q=industry
As you can see it is all out of order.
Does anyone have any way of stopping NSJSONSerialization for the previous connection.
Or possibly any other way to go about this problem?
Steps up to NSJSONSerialization...
NSURLRequest (initwithURL)
NSOperationQueue
NSURLConnection (asynchronous)
NSJSONSerialization
Thanks in advance.
When the user starts typing more text, you could cancel your previous connections and ignore any further delegate callbacks you receive from them. Then make the new request for the current text.
You can do this by maintaining some sort of lastRequest or lastOperation reference. When the user starts typing, call [self.lastRequestOrOperation cancel] and ignore any further notifications from that request with a check like if (request != self.lastRequest) { return; } in whatever callbacks you have.
However this has the problem that if the user keeps typing for a while you are constantly cancelling requests and they may not see any results until they have stopped typing.
A better solution would be to add sequencing so that each request is associated with an increasing sequence ID. You then only parse the result and update the UI when the sequence of the response is higher than the last one you received. If you receive any out-of-band responses from earlier, you just ignore them.
This is a much more complex issue than just being able to cancel the NSJSONSerialization. My suggestion is to use NSFetchedResultsController to populate your table view that shows the search results. Use the search term as one of the predicate variable in the NSFetchRequest attached to NSFetchedResultsController. And then, when you parse the results using NSJSONSerialization, store the results with the search term associated with that request. As soon as the search term changed (which you can detect when the user types more characters), re-create the NSFetchedResultsController and reload your table view. In addition, you can also try to cancel the call to parse the previous results if you launched it using performSelector:withObject:afterDelay. Beware that this cannot be always relied upon as the call may have been initiated by the time you are trying to cancel.
Kinda basic, but you could always maintain an nsdictionary of sub-classed NSURLRequests (sub-classed to provide a tag).
Start request - add request to dicationary with tag = array.count - 1, with key matching tag
Connection returns - is the request the most recent request, if so, parse json
Parse JSON - is the request the most recent request, if so, show results, if not, only display if there are no previous results displayed
Request handling - remove key from dictionary
most recent request = does the dictionary contain an object with a higher key value
Currently what you are doing is, you type each character and calling web-service. Why to call web-service for each letter you type. If user is type continuously, then it will increase the load, so call the web-service only when user stops for a particular interval of time. and then pass that string to call web-service or what ever method you are calling.
[NSObject cancelPerformSelectorsWithTarget:self]; // This will cancel your all req which is going to make when user typing without stopping
[self performSelector:#selector(sendSearchRequest) withObject:searchText afterDelay:0.1f]; // This will pass the string to call a web-service method, on which user hold for some time.