JSON file Download from Server , file is loaded 8 times? - ios

I am loading my LiveRatingDemo.json from the internet server.
Everything works but,
The LiveRatingDemo.json is loaded 8 times from the server why?
What have I forgotten or done wrong?
my LiveRatingDemo.json :
{
"ratingArray":[{
"ratingClubID":"101",
"ratingValue":"2"
},
{
"ratingClubID":"102",
"ratingValue":"4"
},
{
"ratingClubID":"103",
"ratingValue":"5"
},
{
"ratingClubID":"104",
"ratingValue":"1"
},
{
"ratingClubID":"105",
"ratingValue":"3"
},
{
"ratingClubID":"106",
"ratingValue":"5"
},
{
"ratingClubID":"107",
"ratingValue":"4"
}
]
}
my objective c Code :
-(void)parseLiveRating
{
NSURL *baseURL = [NSURL URLWithString:getLiveRating];
NSString *path = [NSString stringWithFormat:#"LiveRatingDemo.json"];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
[manager GET:path parameters:nil progress:nil success:^(NSURLSessionDataTask *task, id JSON)
{
NSDictionary *dictTemp = (NSDictionary *) JSON;
NSArray *arrRating = [dictTemp objectForKey:#"ratingArray"];
NSLog(#"Test Ausgabe : %#", arrRating);
for (int i=0; i<arrRating.count; i++) {
GetRating *rat = [[GetRating alloc] init];
[rat parseResponse:[arrRating objectAtIndex:i]];
[mratingLive addObject:rat];
NSLog(#"Success: %#", JSON);
[self displayAllRatings];
}
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error Loading: %#", error);
}];
}
Thank you for your efforts

I give you the solution.From your question you asked
The LiveRatingDemo.json is loaded 8 times from the server why?
The First Reason is
1.You added or put NSLog(#"Success: %#", JSON) code in for loop.Whenever for loop runs,it prints the result.If you have 8
objects,eight times it prints or displays the result.
So you need to remove the NSLog(#"Success: %#", JSON) from the for loop.
The Second Reason is
2.Then you must remove the [self displayAllRatings] line from the for loop.Becuase I think in [self displayAllRatings] you call the
mratingLive array.So every time that method calls when for loop runs.
Just call that method out of the for loop.

Related

ios - Program with block executed out of order?

I'm trying to get an array of urls from my backend.
I use AFNetworking and I have a HTTPUtil class implemented as singleton to handle my requests.
HTTPUtil.m
#import "HTTPUtil.h"
#implementation HTTPUtil
+(instancetype)sharedInstance{
NSLog(#"sharedInstance"); //to check the order
static HTTPUtil* manager;
static dispatch_once_t once;
dispatch_once(&once, ^{
manager = [[HTTPUtil alloc] init];
});
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
return manager;
}
-(void)getImageArrayFromURL:(NSString *)url success:(void(^)(NSArray* array))success failure:(void(^)(NSError* error))failure{
NSLog(#"getting..."); //to check the order
[self GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask* task, id response){
NSLog(#"Response: %#", response);
NSString* imgStr = [[response objectForKey:kResponseDataKey] objectForKey:#"img"];
//convert nsstring to nsarray
NSArray* array = [StringUtil arrayFromString:imgStr];
//construct urls
NSMutableArray* ret = [[NSMutableArray alloc] init];
NSMutableString* url;
for (NSString* rawStr in array) {
url = [NSMutableString stringWithString:kUrlBase];
[url appendString:[rawStr stringByReplacingOccurrencesOfString:#"/" withString:#"+"]];
[ret addObject:url];
}
success(ret);
}failure:^(NSURLSessionDataTask* task, NSError* error){
NSLog(#"Error: %#", error);
failure(error);
}];
}
In my view controller, I call the method to fetch the array.
_vCycleScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 0, 0, 0) delegate:self placeholderImage:[UIImage imageNamed:#"checked"]];
NSMutableString* url = [NSMutableString stringWithString:kUrlBase];
[url appendString:#"activityImgArray"];
//
__block NSArray* imgarr;
[[HTTPUtil sharedInstance] getImageArrayFromURL:url success:^(NSArray* array){
imgarr = [NSArray arrayWithArray:array];
}failure:^(NSError* error){
NSLog(#"%#", error);
}];
NSLog(#"adding...");
_vCycleScrollView.imageURLStringsGroup = imgarr;
[self.view addSubview:_vCycleScrollView];
[_vCycleScrollView mas_makeConstraints:^(MASConstraintMaker* make){
make.top.equalTo(self.view);
make.left.equalTo(self.view);
make.right.equalTo(self.view);
make.height.mas_equalTo(180);
make.width.equalTo(self.view.mas_width);
}];
In the console, I got
2016-05-20 14:41:19.411 SCUxCHG[10470:4909076] sharedInstance
2016-05-20 14:41:19.415 SCUxCHG[10470:4909076] getting...
2016-05-20 14:41:19.417 SCUxCHG[10470:4909076] adding...
2016-05-20 14:41:19.591 SCUxCHG[10470:4909076]
Response: {
data = {
img = "[activity/test1, acti/1]";
};
message = success;
result = 0;
}
I thought imgArr should be assigned in the success block and it shouldn't be nil when I assign it to _vCycleScrollView.imageURLStringsGroup.
However, I can tell from the output in the console that the HTTP request is sent after NSLog(#"adding..."); and that leads to the fact that imgArr is still nil when _vCycleScrollView.imageURLStringsGroup = imgarr; is executed.
Why is that?
Yes below code is in block so this will continue in background
[[HTTPUtil sharedInstance] getImageArrayFromURL:url success:^(NSArray* array){
imgarr = [NSArray arrayWithArray:array];
}failure:^(NSError* error){
NSLog(#"%#", error);
}];
solution - You should add _vCycleScrollView.imageURLStringsGroup = imgarr; inside of success block because you d0 not know when it will completed Or there is another way you should not call in block or should not create block.
Try bellow:
__block NSArray* imgarr;
[[HTTPUtil sharedInstance] getImageArrayFromURL:url success:^(NSArray* array){
imgarr = [NSArray arrayWithArray:array];
NSLog(#"adding...");
_vCycleScrollView.imageURLStringsGroup = imgarr;
}failure:^(NSError* error){
NSLog(#"%#", error);
}];
The completion block is executed once data is fetched.
In your case code continues to execute after the completion block is set but data hasn't been fetched yet, that's why imgarr is nil.
That's the whole idea: That blocks are executed out of order. The trick is that you don't wait for a block to finish. Instead, the block finishes and then it does what is needed. The code in your viewcontroller isn't going to work, can't work, and we don't want it to work. Instead, the callback block deposits the image somewhere, and then tells the tableview to reload the row.

difficulty understanding objective c code

i have been trying to implement the follow code , but I am having a hard time understanding the following code:
- (void)getRoutesWithStopName:(NSString *) stopName
success:(void (^)(NSArray *routes))success
error:(void (^)(NSString *errorMsg)) error
{
[[self AFManagerObject] POST:GET_ROUTES
parameters:#{#"params" : #{ #"stopName": [NSString stringWithFormat:#"%%%#%%",[stopName lowercaseString]]} }
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *routesRows = responseObject[#"rows"];
NSMutableArray *routes = [[NSMutableArray alloc] initWithCapacity:routesRows.count];
for(NSDictionary *dicRoute in routesRows)
{
FLBRoute *route = [[FLBRoute alloc] initWithAttrs:dicRoute];
[routes addObject:route];
}
success(routes);
}
failure:^(AFHTTPRequestOperation *operation, NSError *err) {
error(err.description);
}
];
}
I tried learning about blocks but I still can not understand what is going on here. Can you provide me a step by step explanation of the code ?
actually here used for webserviceCall
step-1
- (void)getRoutesWithStopName:(NSString *) stopName
success:(void (^)(NSArray *routes))success
error:(void (^)(NSString *errorMsg)) error
// here pass the one NSString and get the response using NSArray and failure using NSString
step-2
// here used AFNEtworking for call web service
//request block
[self AFManagerObject] -- NSObject class for AFNetworking method place.
POST:GET_ROUTES --> post is default function of request Type, GET_ROUTES --> your Macro class for Request URL
parameters --> send the parameter to server
[[self AFManagerObject] POST:GET_ROUTES
parameters:#{#"params" : #{ #"stopName": [NSString stringWithFormat:#"%%%#%%",[stopName lowercaseString]]} }
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
/*********** success response serlize and store into Array**********/
NSArray *routesRows = responseObject[#"rows"];
NSMutableArray *routes = [[NSMutableArray alloc] initWithCapacity:routesRows.count];
for(NSDictionary *dicRoute in routesRows)
{
FLBRoute *route = [[FLBRoute alloc] initWithAttrs:dicRoute];
[routes addObject:route];
// this is your NSObject class for save the details ,
}
success(routes);
/************** success stop **********/
}
/*********** error if request is fail ************/
failure:^(AFHTTPRequestOperation *operation, NSError *err) {
error(err.description);
}
];
/*********** error if request is stop ************/
I think you need to read a little more about callbacks https://en.m.wikipedia.org/wiki/Callback_(computer_programming) and blocks https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html and https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html
Basically the method send a POST request and as you know it needs some time for the request to be sent to the server and for the server to respond. You don't want in this time your application to be freezed, so 2 callbacks are used, 1 for success case and 1 for failure case. A block callback is just a block of code that you want to be executed later, when the server will respond back, being a success or failure.

How to modify a non-local (global) variable from inside of a block?

I am quite new to Objective-C & have to dynamically change the value of #property (strong, nonatomic) NSMutableArray *allCategories from inside of AFHTTPRequestOperationManager in success block.
[self.allCategories addObject:tempObject]; doesn't change the value of allCategories while iterating in a loop.
The variable has been initialized as self.allCategories = [[NSMutableArray alloc]init]; in viewDidLoad.
I have also tried creating a temporary variable as __block NSMutableArray *tempCategories = [[NSMutableArray alloc]init]; before initiating AFHTTPRequestOperationManager object. tempCategories doesn't even retain its value.Can't figure out what's happening.EditSorry for inconvenienceviewDidLoad has the following code self.allCategories = [[NSMutableArray alloc]init];[self loadData];Here's the code
-(NSMutableArray *)loadData
{
__block NSMutableArray *tempCategories = [[NSMutableArray alloc]init];
manager = [AFHTTPRequestOperationManager manager];
[manager GET:kAPICategoryList
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// downcast id to NSMutableDictionary
NSMutableDictionary *json = (NSMutableDictionary *)responseObject;
// check if dictionary is non nil has at least 1 element
if (json != nil && [json count] >= 1) {
// NSLog(#"json:\t%#", json);
// check json is non nil & has success message
if ([json objectForKey:kAPIKeyCategoryRoot] != nil) {
NSArray *arrCategoriesRoot = [json objectForKey:kAPIKeyCategoryRoot];
// check categories has some data
if (arrCategoriesRoot.count >= 1) {
for (int i = 0; i < arrCategoriesRoot.count; i++) {
SomeModel *pCategory;
NSDictionary *dctCategorySingle = [arrCategoriesRoot objectAtIndex:i];
// check category has sub category
if ([dctCategorySingle objectForKey:kAPIKeyCategorySubCategory] != nil) {
// create category with sub category
pCategory = [[SomeModel alloc]initWithSubCategorisedCategoryID:[dctCategorySingle objectForKey:kAPIKeyCategoryID]
name:[dctCategorySingle objectForKey:kAPIKeyCategoryName]
image:kIMGCategoryDefault
subCategory:[dctCategorySingle objectForKey:kAPIKeyCategorySubCategory]];
} else{
// create just a category
pCategory = [[SomeModel alloc]initWithCategoryID:[dctCategorySingle objectForKey:kAPIKeyCategoryID]
name:[dctCategorySingle objectForKey:kAPIKeyCategoryName]
image:kIMGCategoryDefault];
} // else just
[tempCategories addObject:pCategory];
[_allCategories addObject:pCategory];
} // for
NSLog(#"categories count %lu", [self.allCategories count]);
} // if count >= 1
}
else if ([json objectForKey:kAPIRespMsgCategoryFetchErrKey] != nil) {
[Utility showAlertWithTitle:kAPIRespMsgCategoryFetchErrKey
message:[json objectForKey:kAPIRespMsgCategoryFetchErrVal]
button:kMsgButtonOkayTtl];
}
} else {
// error in login => enable login
NSLog(#"%#", kMsgNetworkEmptyJSON);
}
}
// network error
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error %#", [error localizedDescription]);
}];
NSLog(#"tempCategories count %lu", [tempCategories count]);
return tempCategories;
}
Here's the output form NSLog:2015-03-19 18:27:17.845 MyProject[4011:121268] viewDidLoad
2015-03-19 18:27:18.133 MyProject[4011:121268] tempCategories count 0
2015-03-19 18:27:18.136 MyProject[4011:121268] numberOfRowsInSection count 0
2015-03-19 18:27:18.137 MyProject[4011:121268] numberOfRowsInSection count 0
2015-03-19 18:27:19.019 MyProject[4011:121268] categories count 20when loadData finishes allCategories has not data in it (nil).
As far as I know it should work that way.. are you sure your success block is being called before you check the content of allCategories?
A success block work asynchronously, which means it will be executed only when the RequestOperationis completed (which can take a long time if you're downloading something big)
If you are trying to get the value of allCategories before the success block is executed you won't get what you're expecting. I would recommend using breakpoints or NSLog on your success block to see if it's been executed when you think it's doing it.
e.g
...
successBlock:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Success");
[self.allCategories addObject:tempObject]
}]; //End of request
[operation start]; //Begin executing the AFHTTPOperation
NSLog("%#",self.allCategories.description); //probably nil or empty
//since the success block hasn't been called yet
EDIT:
As I though, you are returning a value before is been set by the async operation, to return a value from an async operation I would suggest take a look to this answer and this one . Also you should read a bit of how async task work.
Basically what you want to do with async operations/tasks is make sure the value will be available when you want to use it. The main issue with that is that you don't know when the value will be set, but you can make sure what you want to do whenever it's set.
To do that you can create a simple method with a custom completion block
- (void)myCustomMethodWithCompletionBlock: (void (^)(NSArray *))completion {
//Do your request
//...
successBlock:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Success");
completionBlock(allCategories);
}]; //End of request
}
Meanwhile in your main method you call
[self myCustomMethodWithCompletionBlock:^(NSArray *allCategories) {
self.allCategories = allCategories;
//Do other stuff you need to with that variable since now you are
//sure the value will be set unless the operation failed
}];
I had the same problem a few days ago. My problem was my array seems nil, array allocations in viewdidload method may be your request run before viewDidLoad. Check it with debug if you see the array is nill then alloc array different place.
P.S: I m not expert but may be it's the same problem with me.
Try this:
dispatch_async(dispatch_get_main_queue(), ^{
[self.allCategories addObject:tempObject];
});
Define NSMutableArray with following line.
#property (nonatomic, strong) NSMutableArray * arrData;
initializein viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
self.arrData = [NSMutableArray array];
}
call following method with any UIButton action for see output OR working behavior
- (void) TestMethod {
dispatch_queue_t queue = dispatch_queue_create("myQueue", 0);
dispatch_async(queue, ^{
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL urlWithEncoding:#"https://www.google.co.in/?gws_rd=ssl"]];
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[httpClient setDefaultHeader:#"Accept" value:#"application/json"];
[httpClient setParameterEncoding:AFJSONParameterEncoding];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:#"" parameters:nil];
[request setTimeoutInterval:180];
[AFJSONRequestOperation addAcceptableContentTypes:[NSSet setWithObject:#"text/html"]];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON)
{
[self.arrData addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"test",#"t3da",#"adsf",#"afds", nil]];
dispatch_semaphore_signal(sema);
} failure:^ (NSURLRequest *request, NSURLResponse *response, NSError *error, id json){
[self.arrData addObject:[NSDictionary dictionaryWithObjectsAndKeys:#"test",#"t3da",#"adsf",#"afds", nil]];
dispatch_semaphore_signal(sema);
}];
[operation start];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
DLog(#"arrData = %#",self.arrData);
});
}

How to Fetch data out from block in AFNetworking ios?

Creating first app with webservices, I am using AFNetworking for webservices. Everything is working fine but i have no idea , that how to fetch data out from block which i am getting in response. This is what i have done so far
+(WebServices *)sharedManager{
static WebServices *managerServices = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
managerServices = [[self alloc] init];
});
return managerServices;
}
-(NSArray *)firstPostService{
//1
NSURL *url = [NSURL URLWithString:BaseURLString];
//2
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSDictionary *param = #{#"request" : #"get_pull_down_menu" , #"data" : #"0,0,3,1"};
[manager POST:#"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
[self methodUsingJsonFromSuccessBlock:responseObject];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[av show];
}];
if (list.count == 0) {
NSLog(#"Nothing in array yet!!");
}
else{
NSLog(#"Object 1 is : %#", [list objectAtIndex:1]);
}
return list;
}
- (void)methodUsingJsonFromSuccessBlock:(id)json {
// use the json
NSString *string = [NSString stringWithUTF8String:[json bytes]];
NSLog(#"This is data : %#", string);
list = [string componentsSeparatedByString:#"\n"];
NSLog(#"After sepration first object: %#", [list objectAtIndex:1]);
//NSLog(#"json from the block : %#", json);
}
What i understand reading from different blogs and tuts, that block is a separate thread and what every i do finishes with it. I read some where that this is normally use for it
dispatch_async(dispatch_get_main_queue(), ^{
data = [string componentsSeparatedByString:#"\n"];
//WHERE DATA IS __block NSArray * data = [[NSArray alloc] init];
});
and i was returning it in the of the function(firstPostService) but nothing happen. i still get an empty array outside the block. Kindly help me , suggest me some good reading stuff. Thanking you all in advance.
You say:
I need this data to my view controller i am trying to return in dispatch part but it is not allowing. Is it possible to get data into my viewcontroller class ?
Yes, it's possible. But, no, firstPostService should not return the results. It can't because it returns immediately, but the POST completion blocks won't be called until much later. There's nothing to return by the time firstPostService returns.
At the end of your original question, you said:
What i understand reading from different blogs and tuts, that block is a separate thread and what every i do finishes with it. I read some where that this is normally use for it
dispatch_async(dispatch_get_main_queue(), ^{
data = [string componentsSeparatedByString:#"\n"];
//WHERE DATA IS __block NSArray * data = [[NSArray alloc] init];
});
This is not the appropriate pattern of __block local variable. You generally use that __block pattern when dealing with some block that runs synchronously (for example the block of an enumeration method). But while you can use __block variable with asynchronous block, you almost never do (and it doesn't quite make sense to even try to do it). When you use appropriate completion block patterns, there's no need for any __block variable.
So, let's go back to your original code sample: So, you should take a page from AFNetworking and employ completion blocks yourself. When the AFNetworking POST method wanted to return data to your code asynchonously, it used a completion block pattern, instead. Thus, if your own firstPostService wants to pass back data asynchronously, it should do the same.
For example:
#interface WebServices ()
#property (nonatomic, strong) AFHTTPSessionManager *manager;
#end
#implementation WebServices
// note, use `instancetype` rather than actually referring to WebServices
// in the `sharedManager` method
+ (instancetype)sharedManager
{
static id sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
// I'd also suggest that you init the `AFHTTPSessionManager` only once when this
// object is first instantiated, rather than doing it when `firstPostService` is
// called
- (instancetype)init
{
self = [super init];
if (self) {
NSURL *url = [NSURL URLWithString:BaseURLString];
self.manager = [[AFHTTPSessionManager alloc] initWithBaseURL:url];
self.manager.responseSerializer = [AFHTTPResponseSerializer serializer];
}
return self;
}
// Notice:
//
// 1. This now has a return type of `void`, because when it instantly returns,
// there is no data to return.
//
// 2. In order to pass the data back, we use the "completion handler" pattern.
- (void)firstPostServiceWithCompletionHandler:(void (^)(NSArray *list, NSError *error))completionHandler {
NSDictionary *param = #{#"request" : #"get_pull_down_menu" , #"data" : #"0,0,3,1"};
[self.manager POST:#"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
NSArray *list = [self methodUsingJsonFromSuccessBlock:responseObject];
if (completionHandler) {
completionHandler(list, nil);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[[[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
// // none of this code belongs here!!! You are dealing with asynchronous methods.
// // the `list` has not been returned by the time you get here!!! You shouldn't even
// // be using instance variable anyway!
//
// if (list.count == 0) {
//
// NSLog(#"Nothing in array yet!!");
// }
// else{
// NSLog(#"Object 1 is : %#", [list objectAtIndex:1]);
//
// }
// return list;
}
- (NSArray *)methodUsingJsonFromSuccessBlock:(NSData *)data {
// note, do not use `stringWithUTF8String` with the `bytes` of the `NSData`
// this is the right way to convert `NSData` to `NSString`:
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"This is string representation of the data : %#", string);
// Note, retire the `list` instance variable, and instead use a local variable
NSArray *list = [string componentsSeparatedByString:#"\n"];
NSLog(#"After sepration first object: %#", [list objectAtIndex:1]);
return list;
}
#end
Then, you could invoke that like so:
[[WebServices sharedManager] firstPostServiceWithCompletionHandler:^(NSArray *list, NSError *error) {
if (error) {
// handle the error here
} else {
// use the `list` results here
}
}];
// NOTE, DO NOT USE `list` HERE. By the time you get here, `list` has not been
// returned. Only use it in the above block.
//
// In fact, you can see that if you put a `NSLog` or breakpoint here, and again, above
// where it says "use the `list` results` here", you'll see that it's running the code
// inside that block _after_ this code down here!
I'd suggest you tackle the above first, to first make sure you completely understand the proper asynchronous technique of the completion block pattern. We don't want to complicate things quite yet. Make sure you're getting the sort of data you wanted before you proceed to what I will describe below.
But, once you've grokked the above, it's time to look at your JSON parsing. You make several reference to JSON, but if that's what it really is, then using componentsSeparatedByString is not the right way to parse it. You should use NSJSONSerialization. Or even better, you can let AFNetworking do that for you (right now, you're making it more complicated than it needs to be and your results will not be formatted correctly).
Above, I kept your methodUsingJsonFromSuccessBlock in the process, but if you're really dealing with JSON, you should eliminate that method entirely. Let AFNetworking do this for you.
You should eliminate the line that says:
responseSerializer = [AFHTTPResponseSerializer serializer];
The default serializer is AFJSONResponseSerializer which is what you want to use if handling JSON requests.
The methodUsingJsonFromSuccessBlock is then no longer needed because AFNetworking will do the JSON conversion for you. So firstPostServiceWithCompletionHandler should look like:
- (void)firstPostServiceWithCompletionHandler:(void (^)(NSArray *list, NSError *error))completionHandler {
NSDictionary *param = #{#"request" : #"get_pull_down_menu" , #"data" : #"0,0,3,1"};
[self.manager POST:#"person.php" parameters:param success:^(NSURLSessionDataTask *task, id responseObject) {
if (completionHandler) {
completionHandler(responseObject, nil);
}
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[[[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
if (completionHandler) {
completionHandler(nil, error);
}
}];
}

How to slow down ios process

I have an app that can send information to a server. This information is stacked up during the day (while the client uses the app), and when he so desires, he can hit the "update" button to send everything on the server.
This always worked fine until he recently had a flow increase and went from updating 10 objects to more than 100.
Obviously, the update takes more time, taht's not the issue.
The issue is, at some point, i'm getting
Error: Error Domain=NSURLErrorDomain Code=-1001 "La requête a expiré."
UserInfo=0x189874b0 {NSErrorFailingURLStringKey=http://www.*********.be/upload,
NSErrorFailingURLKey=http://www.************.be/upload,
NSLocalizedDescription=La requête a expiré.,
NSUnderlyingError=0x189abd70 "La requête a expiré."}
For the frenchophobes, " The request has expired " is what i get back, and i've hidden the url with ****, as you noticed.
Now, i've tried locally, it works fine with a small update, but when i loop 150 times on my update (i send 150 times the same thing), at some point i just get the above error X times. This error does not specificall occur with all the last items, it can be 20 in the middle, or 30, etc.
Is there a way i can change that?
Here is a piece of code that must be related to the issue.
// Set the max number of concurrent operations (threads)
//[operationQueue setMaxConcurrentOperationCount:3]; // Todo: try increasing max thread count
[operationQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount]; //dynamic thread count
self.queueCount = persons.count;
self.currentQueue = 1;
for (Person *person in persons) {
for (int i = 0 ; i<130 ; i++){ //this is where i try to break the app
[self createSendPersonOperation:person];
}}
Now what would probably work is put the last line in a "thing" that would slow down the process every 20 or so occurences, so the server or the app doesn't go crazy.
Is this possible? if so, how?
Note : I am a junior dev trying to get into a senior's code, and that guy is not available, so i'm open to all the help i can have.
Edit : also, do you think my error comes from a server-sided issue or is definitly an app-sided issue?
Edit : Complete HTTP request.
So for every person that is saved into the app, when the user decides to update, it does that for every Person in the array of persons.
- (void)createSendPersonOperation:(Person *)person
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:#"text/html", #"application/json", nil];
NSDictionary *params = #{
#"email": person.email,
#"gender": person.gender,
#"language": person.language,
#"hasFacebook": person.hasFacebook,
#"sendPostalCard": person.sendPostalCard
};
NSLog(#"params: %#", params);
[manager POST:kURLUpdate parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// Add picture to the form
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pictureFilePath = [documentsDirectory stringByAppendingPathComponent:person.picture];
NSURL *pictureURL = [NSURL fileURLWithPath:pictureFilePath];
[formData appendPartWithFileURL:pictureURL name:#"picture" error:nil];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success: %#", responseObject);
if ([responseObject isKindOfClass:[NSDictionary class]]) {
if ([responseObject objectForKey:#"error"]) {
NSLog(#"Error 1");
NSDictionary *error = [responseObject objectForKey:#"error"];
NSLog(#"Error message: %#", [error objectForKey:#"message"]);
} else {
// Set Person's sended attribute
person.sended = #YES;
[Person saveObject:[[PersistentStack sharedInstance] managedObjectContext] error:nil];
}
} else {
NSLog(#"Error 2");
}
[self decreaseQueueCount];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
NSLog(#"Parameter that failed : %#", [params objectForKey:#"email"]);
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Erreur"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Fermer"
otherButtonTitles:nil];
[alertView show];
self.updateHud.mode = MBProgressHUDModeText;
self.updateHud.labelText = AMLocalizedString(#"update.failure.message", #"");
[self.updateHud hide:YES afterDelay:3];
}];
}
I don't really know the source of your problem, but if you think slowing the app will at least help you understand your problem you could do it with something like this:
NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:15];
while ([loopUntil timeIntervalSinceNow] > 0) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:loopUntil];
}
It will wait for 15 seconds before continue, so you can put this one after 20~30 requests as you suggested.
I really believe you should consider grouping your requests or something like that so you won't overload your server (if that is really your problem).

Resources