I've 2 web-services, on of them retrieve all the images and other retrieve filtered images from web service.
When the app loads it call web service which retrieve all the images. And when user apply filters it retrieve the filtered images. But the problem I'm facing is:
Problem Statement:
When user select at least one filter it worked fine. But when user un-select (means none of the filters are selected) it goes to failure. My web service is coded in a way that when no parameters are passed it should return all the images, but it didn't. I want it to load the all images web-serivce again.
With Code Explanation:
[operation GET:#"stock_search" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
// operation is AFHTTPRequestOperationManager
NSMutableArray *temGalArray = [responseObject objectForKey:#"data"];
[imageArray removeAllObjects];
for (NSDictionary *myDict in temGalArray)
{
id object = [myDict objectForKey:#"square_image"];
if ([myDict objectForKey:#"square_image"]!=[NSNull null])
{
[imageArray addObject:myDict]; //this works fine
}
else if([object isEqual:[NSNull null]])
{
[self getGalleryFromWeb]; //***PROBLEM IS HERE***
//1: This condition is never true
//2: Self.getGalleryFromWeb is the webserivce that get
// all the images from web. There is no issue in that webservice
}
}
[galleryView reloadData];
}
//It always loads failure code below
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error Applying Filters"
message:#"Check Your Internet Connection"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}];
}
So what should I write under `else if' so that if no filter value is selected it load all the images again just like when the app loads. I hope I've cleared my problem as it is my first question so if there is anything that I miss I'm ready to provide.
When you got selection at that time check your filtered Array count and if it is >0 then don't call any web-service.
In this way your previously loaded images will not refresh.Just call filtered webservice only when arrays count is greater than >0 and after that reload your data.
Related
Don't know what to do with it. but i loaded data and load table when data is loaded,
[manager POST:path parameters:parameters success:^(NSURLSessionDataTask *task, id responseObject) {
arryGlobal = [NSMutableArray new];
[arryGlobal addObject:responseObject];
if([[[[arryGlobal valueForKey:#"Success"] objectAtIndex:0] stringValue] isEqualToString:#"1"]){
arryGlobal = [[arryGlobal valueForKey:#"Result"] objectAtIndex:0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tblMainCategory reloadData];
});
}
else if([[[[arryGlobal valueForKey:#"Success"] objectAtIndex:0] stringValue] isEqualToString:#"0"]){
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
//here is place for code executed in error case
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error while sending"
message:#"Sorry, try again."
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
NSLog(#"Error: %#", [error localizedDescription]);
}];
It works perfectly two times, but when i go for 3rd time call this webservice it load data, get data, table successfully reloaded, but it not change content in table, it appear as it is in 2nd time.
SO what happens there ?? When I scroll table, then in cellForRowAtIndexPath array that i use to pass in table is contain data of 2nd time called Webservice.
EDIT:
i added this table view VievController in other view like :
MainCategory *objMain = [[MainCategory alloc] initWithNibName:#"MainCategory" bundle:nil];
[objMain LoadData:tag];
objMain.view.frame = CGRectMake(0, 0, self.bgViewCat.frame.size.width, self.bgViewCat.frame.size.height);
[self.bgViewCat insertSubview:objMain.view atIndex:1];
[self addChildViewController:objMain];
[objMain didMoveToParentViewController:self];
Try This
if([[[[arryGlobal valueForKey:#"Success"] objectAtIndex:0] stringValue] isEqualToString:#"1"])
{
[arryGlobal removeAllObjects];
arryGlobal=[NSarray alloc]init];
arryGlobal = [[arryGlobal valueForKey:#"Result"] objectAtIndex:0];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tblMainCategory reloadData];
});
}
the block:success() already run in main queue;you needn't use dispatch_async(dispatch_get_main_queue();
and arryGlobal is a kind of NSMutableArray;why you use the function called "thevalueforKey:" to get the value ?
I think maybe you should print the responseObject to check your data
ok so ther's error on :
[self.bgViewCat insertSubview:objMain.view atIndex:1];
Instead of this I use this and everything is fine, don't know why?:
[self.bgViewCat addSubview:objMain.view];
I am using Azure Mobile Service as a backend for an iOS application. I have set up everything to work with offline sync which allows me to view, add, or modify data even when there is no network connection. I am running into a problem when I add a new object into a table. The add works well locally but when I synchronize data it creates a duplicate item on the local database with a slightly different objectId. The created item is not duplicated on the server side.
Here's how I am setup. By the way, thanks to #TheBasicMind for posting this model.
Here's a link to his explanation of the model: enter link description here
Here's what I do to setup the sync context and sync table:
// Initialize the Mobile Service client with your URL and key
MSClient *client = self.hpc.client;
NSManagedObjectContext *context = self.hpc.syncContext;
MSCoreDataStore *store = [[MSCoreDataStore alloc] initWithManagedObjectContext:context];
client.syncContext = [[MSSyncContext alloc] initWithDelegate:syncDelegate dataSource:store callback:nil];
// Add a Mobile Service filter to enable the busy indicator
self.client = [client clientWithFilter:self];
// Create an MSSyncTable instance to allow us to work with the Athlete table
self.syncAthleteTable = [self.client syncTableWithName:#"Athlete"];
Here's how I add a record for the moment:
NSDictionary *newItem = #{#"firstname": firstname, #"lastname": lastname, #"laterality" : laterality};
[self.athletesService addItem:newItem completion:^{
NSLog(#"New athlete added");
}];
-(void)addItem:(NSDictionary *)item completion:(CompletionBlock)completion
{
// Insert the item into the Athlete table
[self.syncAthleteTable insert:item completion:^(NSDictionary *result, NSError *error)
{
[self logErrorIfNotNil:error];
// Let the caller know that we finished
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}];
}
The add works as expected and it is added in a UITableView as I have an NSFetchedResultsController listening on my Main Context.
Here's where the problem occurs. When I synchronize data with the server using this function:
-(void)syncData:(CompletionBlock)completion
{
// push all changes in the sync context, then pull new data
[self.client.syncContext pushWithCompletion:^(NSError *error) {
[self logErrorIfNotNil:error];
[self pullData:completion];
}];
}
-(void)pullData:(CompletionBlock)completion
{
MSQuery *query = [self.syncAthleteTable query];
// Pulls data from the remote server into the local table.
// We're pulling all items and filtering in the view
// query ID is used for incremental sync
[self.syncAthleteTable pullWithQuery:query queryId:#"allAthletes" completion:^(NSError *error) {
[self logErrorIfNotNil:error];
[self refreshDataOnSuccess:completion];
}];
}
- (void) refreshDataOnSuccess:(CompletionBlock)completion
{
MSQuery *query = [self.syncAthleteTable query];
[query readWithCompletion:^(MSQueryResult *results, NSError *error) {
[self logErrorIfNotNil:error];
NSLog(#"Data that pulled from local store: ");
for ( NSDictionary *dict in results.items ) {
NSLog(#"%# %#", [dict objectForKey:#"firstname"], [dict objectForKey:#"lastname"] );
}
// Let the caller know that we finished
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}];
}
After the synchronization the NSFetchedResultsChangeInsert is called a second time for the same record with a slightly different objectID. Here's an example of the first and second objectIDs:
tD7ADE77E-0ED0-4055-BAF6-B6CF8A6960AE9
tD7ADE77E-0ED0-4055-BAF6-B6CF8A6960AE11
I am stuck here.
Any help is highly appreciated. Thank you!
In the past, when I've seen this happen, its because the "id" field the client is sending was being changed or ignored by the server logic.
Locally the store finds the object in core data using that field, so a change to it could result in the client SDK thinking it needs to insert a new object and not update an existing one.
One easy way to confirm this, is by using the tableOperation:complete: method on the data delegate and comparing the "id" column between the item originally and that being returned by operation execute.
I am having an issue parsing two JSON urls at once. YouTube only permits 50 results per request, so I'd like to add a second with a start-index of 51, to continue the request.
NSString *urlAsString = #"https://gdata.youtube.com/feeds/api/playlists/PLgw1uRYia2CRvuF4Y3KLuvFSWY6lmuY8T?v=2&alt=json&max-results=50&orderby=published";
NSString *urlAsString2 = #"https://gdata.youtube.com/feeds/api/playlists/PLgw1uRYia2CTSBBNrTDjdEcswVFjPkCr9?v=2&alt=json&max-results=50&orderby=published";
Combining two of them, I tried this:
NSString *finallink = [NSString stringWithFormat:#"%#,%#", urlAsString, urlAsString2];
Then making the actual request with Afnetworking, I added:
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:finallink parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSDictionary *feed = [[NSDictionary alloc] initWithDictionary:[responseObject valueForKey:#"feed"]];
videoArray = [NSMutableArray arrayWithArray:[feed valueForKey:#"entry"]];
[self.videoMetaData addObjectsFromArray:[videoArray valueForKeyPath:#"title.$t"]];
operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
[self.videolist reloadData];
[self->activityind startAnimating];
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Videos"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
NSLog(#"Error: %#", error);
}];
This does not work for some reason. I get this error:
Error: Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)"
What could be wrong?!
As mentioned in the comments, this code fails because you are trying to retrieve the result of two URLs joined to one another. The way it is set up now is equivalent to trying to visit http://google.com,http://google.com in a web browser, which will of course fail.
Instead, the solution is to retrieve the results in batches, one after the other. Here's one way to do it:
Write a method which retrieves the YouTube results at a given offset. If you want to retrieve all links starting at 51, then a good idea would be to have a method which takes an offset and returns the results in a completion block.
Write another method which can use the previous one to retrieve the entire list of results. This will need to send multiple network requests to YouTube, one for each batch of 50 that you need, and collect the results somewhere.
There are a couple other issues I noticed in your code sample. One is that you are setting a new request and response serializer in the success block of your network request – instead, you should set these once somewhere in your app, because reallocating them after each request is inefficient. AFHTTPRequestOperationManager does create default instances of these, so you can get away with not setting them at all.
Another potential issue is that you are displaying an alert view in your failure block. Because AFNetworking performs network requests on a background thread by default (from what I recall), you might run into some weird problems (the usual symptom is that your UI will not show up for a few seconds). Apple requires that UI-related methods are called on the main thread.
Currently, I am attempting to optimize my getMutualFriends method. When I open my 'Friends' view controller, I execute the getMutualFriends method for every friend the user currently has... Which is NOT optimal...but was the easiest solution...
Heres what I did:
[CBUtility queryForFriends:[PFUser currentUser] block:^(NSArray *friends, NSError *error) {
[self.friendsActivityIndicator stopAnimating];
if (error) {
[[[UIAlertView alloc] initWithTitle:#"Error"
message:[error localizedDescription]
delegate:nil cancelButtonTitle:#"Okay"
otherButtonTitles:nil]
show];
return;
}
if ([friends count] == 0 || !friends) {
[self.friendsTable addSubview:self.friendsEmptyView];
return;
}
self.friends = [NSMutableArray arrayWithArray:friends];
[self.friendsTable reloadData];
[self.friendsEmptyView removeFromSuperview];
int i = 0;
//
// THIS IS THE PART THAT SUCKS!!!
//
for (PFObject * friendObject in self.friends) {
[CBUtility queryForMutualFriends:[friendObject objectForKey:kCBFriendToUserKey] block:^(int mutualFriends, NSError *error) {
if (error) {
[[[UIAlertView alloc] initWithTitle:#"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil] show];
return;
}
CBFriendsCell *cell = (CBFriendsCell *)[self.friendsTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
[cell setMutualFriends:mutualFriends];
}];
i++;
}
}];
And heres what +(void)queryForMutualFriends looks like:
+ (void)queryForMutualFriends:(PFUser *)user block:(void (^)(int number, NSError *error))completionBlock
{
PFQuery *usersFriends = [PFQuery queryWithClassName:kCBFriendClassKey];
[usersFriends whereKey:kCBFriendFromUserKey equalTo:user];
[usersFriends whereKey:kCBFriendStatusKey equalTo:kCBFriendStatusFriendKey];
PFQuery *currentUsersFriends = [PFQuery queryWithClassName:kCBFriendClassKey];
[currentUsersFriends whereKey:kCBFriendFromUserKey equalTo:[PFUser currentUser]];
[currentUsersFriends whereKey:kCBFriendStatusKey equalTo:kCBFriendStatusFriendKey];
[currentUsersFriends whereKey:kCBFriendToUserKey matchesKey:kCBFriendToUserKey inQuery:usersFriends];
[currentUsersFriends countObjectsInBackgroundWithBlock:^(int number, NSError *error) {
if (!error) {
completionBlock(number, error);
return;
}
completionBlock(-1, error);
}];
}
So instead of running the loop and passing individual PFUser objects into the getMutualFriends method, I'd like to pass an array of friends into the method and return an array of dictionary objects whose keys are 'user' and 'count' with their respective values (e.g. #[#{#"user":somePFUser, #"count":5}, #{#"user":anotherPFUser, #"count":20}];
I mean, this works fine at the moment but takes up way too much API requests...
Anyone got ideas with how to setup the PFQuery?
EDIT:
Here was a link to a SQL query that solves the same problem
No apparently, you cannot... But you can limit the amount of times you query to the server by instead of querying for mutual friends when you retrieve the mutual friends like I did, you instead cache the results into memory...
I solved this issue by making the query in cellForIndexPath when setting a cells attributes. When the cell is loaded, I check cache first to see if the query has already been made, if it has then I get the cache data... If it hasn't then I make a query to the servers... Only issue I see is that it doesn't update... I figure I can clear cache every minute or so, so the user gets updated automatically instead of pressing a reload button.
I have method called collectData in my app which is the most important part of my View Controller. In that method I do a couple of signicant things (downloading, parsing, saving to persistent store), so it would be easier for you to take a look:
-(void)collectData
{
// Downloading all groups and saving them to Core Data
[[AFHTTPRequestOperationManager manager] GET:ALL_GROUPS parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSMutableDictionary* groups = [NSMutableDictionary new];
NSMutableArray* newIds = [NSMutableArray new];
NSError *error;
// Saving everything from response to MOC
for (id group in responseObject) {
Group *groupEntity = [NSEntityDescription insertNewObjectForEntityForName:#"Group" inManagedObjectContext:self.moc];
groupEntity.name = [group valueForKey:#"name"];
groupEntity.cashID = [group valueForKey:#"id"];
groupEntity.caseInsensitiveName = [[group valueForKey:#"name"] lowercaseString];
groupEntity.selected = #NO;
// Filling up helping variables
groups[groupEntity.cashID] = groupEntity;
[newIds addObject:groupEntity.cashID];
}
// Fetching existing groups from Persistant store
NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:#"Group"];
[r setIncludesPendingChanges:NO];
r.predicate = [NSPredicate predicateWithFormat:#"cashID IN %#",newIds];
NSArray *existingGroups = [self.moc executeFetchRequest:r error:&error];
// Deleting groups which already are in database
for (Group* g in existingGroups) {
Group* newGroup = groups[g.cashID];
g.name = [newGroup valueForKey:#"name"];
g.cashID = [newGroup valueForKey:#"cashID"];
g.caseInsensitiveName = [[newGroup valueForKey:#"name"] lowercaseString];
[self.moc deleteObject:newGroup];
}
// Saving Entity modification date and setting it to pull to refresh
[self saveModificationDate:[NSDate date] forEntityNamed:#"Group"];
[self.pullToRefreshView.contentView setLastUpdatedAt:[self getModificationDateForEntityNamed:#"Group"]
withPullToRefreshView:self.pullToRefreshView];
// Save groups to presistant store
if (![self.moc save:&error]) {
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
[[self fetchedResultsController] performFetch:&error];
[self.pullToRefreshView finishLoading];
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Show alert with info about internet connection
[self.pullToRefreshView finishLoading];
UIAlertView *internetAlert = [[UIAlertView alloc] initWithTitle:#"Ups!" message:#"Wygląda na to, że nie masz połączenia z internetem" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[internetAlert show];
}];
}
So when I start collecting data (first run or push to refresh) this method is blocking UI.
I want to avoid this but when I put the success block into another dispatch_async and get back to main queue only for [self.tableView reloadData] I face problem with saving to persistent store or something with bad indexes.
How can I do this whole thing in background and leave UI responsive to the user?
Just an idea, give it a try using dispatch_sync. Have a look at this explanation here where log result something similar to your need. Put [yourTableView reloadData] after synchronous block.
Hope it helps!
It seems AFNetwork call is not async so just try to call your method via performselector.