Here is where I call the class method. The call is made after a NSURLRequest is finished. All values are there, nothing is nil
[MemberInfo SetMemberInfo:memberId groupId:groupId token:token withContext:_context];
Here is the method implemented in the class generated by the core data "MemberInfo+CoreDataProperties.m"
+ (bool)SetMemberInfo:(NSString *)memberId groupId:(NSString *)groupId token:(NSString *)token withContext:(NSManagedObjectContext *)context
{
NSManagedObject *memberInfoObject = [NSEntityDescription insertNewObjectForEntityForName:#"MemberInfo" inManagedObjectContext:context];
[memberInfoObject setValue:memberId forKey:#"memberId"];
[memberInfoObject setValue:groupId forKey:#"groupId"];
[memberInfoObject setValue:token forKey:#"token"];
NSError *error = nil;
if (![context save:&error])
{
return false;
}
return true;
}
I have zero errors, and nothing in the logs that explains why. But this class method 'SetMemberInfo' is never hit. Any clues?
EDIT **
Full code where I call method
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if ([httpResponse statusCode] == 200)
{
id object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if ([object isKindOfClass:[NSDictionary class]] && error == nil)
{
NSString *groupId = _tfGroupId.text;
NSString *memberId = _tfMemberId.text;
NSString *token = [object valueForKey:#"token"];
[MemberInfo SetMemberInfo:memberId groupId:groupId token:token withContext:_context];
}
}
}
}];
[postDataTask resume];
Must be something to do with the class that I has the class method in. I moved it to another class and it now makes the call.
Related
I'am using [NSURLSession sharedSession] dataTaskWithRequest to get data from webserver and display it in a label & set button images according to it
But my problem is first label code is executed and they are displayed as empty then async block code is finished.
if(![self connected])
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
arrMyres = [[NSMutableArray alloc] initWithArray:[prefs objectForKey:#"Myres"]];
}
else
{
// POSTrequest with URL to get data from server
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:
^(NSData * _Nullable responseData,
NSURLResponse * _Nullable urlResponse,
NSError * _Nullable error) {
if (error) {
//Display error
}
else
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)urlResponse;
if([httpResponse statusCode ]>=200 && [httpResponse statusCode]<300)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
NSArray *array=[[dataDictionary objectForKey:#"GetExistingFavorites"] isKindOfClass:[NSNull class]]? nil:[dataDictionary objectForKey:#"GetExistingFavorites"];
arrMyres=[[NSMutableArray alloc]initWithArray:array];
});
}
}
}] resume];
}
//This block of code executes first and displays empty label
if([arrMyres count]>0 )
{
//Set Button Image and display label
}
else
{
// Do something else
}
How to wait for asynchrous request to complete execution and use it results somewhere after it? During research I found about dispatch groups and completion handlers. but couldnt understand how to implement
Any suggestions would be helpful.
Update firstLabel UI code inside of async code using main thread. Refer below code
if(![self connected])
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
arrMyres = [[NSMutableArray alloc] initWithArray:[prefs objectForKey:#"Myres"]];
[self updateUI];
}
else
{
// POSTrequest with URL to get data from server
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:
^(NSData * _Nullable responseData,
NSURLResponse * _Nullable urlResponse,
NSError * _Nullable error) {
if (error) {
//Display error
}
else
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)urlResponse;
if([httpResponse statusCode ]>=200 && [httpResponse statusCode]<300)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
NSArray *array=[[dataDictionary objectForKey:#"GetExistingFavorites"] isKindOfClass:[NSNull class]]? nil:[dataDictionary objectForKey:#"GetExistingFavorites"];
arrMyres=[[NSMutableArray alloc]initWithArray:array];
//This block of code executes first and displays empty label
if([arrMyres count]>0 )
{
[self updateUI];
}
}
}
}] resume];
}
-(void)updateUI {
dispatch_async(dispatch_get_main_queue(), ^{
//update UI in main thread.
//Add your ui updates
});
}
I have another very beginner's question related to xCode. I am completely new to iOS development so I appreciate you guys to reply me.
I have written the following class to access the Restful API. The code in the method "makePostRequest" works fine if I write it directly in the calling method. But, I want to make it asynchronous and I don't know exactly how can I make this work asynchronous. Can somebody help me please to write this as asynchronos call?
#import <Foundation/Foundation.h>
#import "ServerRequest.h"
#import "NetworkHelper.h"
#implementation ServerRequest
#synthesize authorizationRequest=_authorizationRequest;
#synthesize responseContent=_responseContent;
#synthesize errorContent=_errorContent;
#synthesize url=_url;
#synthesize urlPart=_urlPart;
#synthesize token=_token;
- (void)makePostRequest : (NSString *) params {
NSString *urlString = [NSString stringWithFormat:#"%#%#", [self getUrl], [self getUrlPart]];
NSURL *url = [NSURL URLWithString:urlString];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
if([self isAuthorizationRequest]) {
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setValue:#"Basic" forHTTPHeaderField:#"Authorization"];
}
else {
NSString *authorizationValue = [NSString stringWithFormat:#"Bearer %#", [self getToken]];
[request setValue:authorizationValue forHTTPHeaderField:#"Authorization"];
}
if(params.length > 0)
[request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];
#try {
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error) {
NSLog(#"Error: %#", error);
}
if([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if(statusCode == [NetworkHelper HTTP_STATUS_CODE]) {
self.responseContent = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:nil];
}
else {
self.errorContent = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:nil];
}
}
}];
[dataTask resume];
}
#catch (NSException *exception) {
NSLog(#"Exception while making request: %#", exception);
} #finally {
NSLog(#"finally block here");
}
}
- (void)setAuthorization : (bool)value {
self.authorizationRequest = &value;
}
- (bool)isAuthorizationRequest {
return self.authorizationRequest;
}
- (NSDictionary *)getResponseContent {
return self.responseContent;
}
- (NSDictionary *)getErrorContent {
return self.errorContent;
}
- (void)setToken:(NSString *)token {
self.token = token;
}
- (NSString *)getToken {
return self.token;
}
- (void)setUrl:(NSString *)value {
//self.url = value;
_url = value;
}
- (NSString *)getUrl {
return self.url;
}
- (void)setUrlPart:(NSString *)value {
self.urlPart = value;
}
- (NSString *)getUrlPart {
if(self.urlPart.length == 0)
return #"";
return self.urlPart;
}
#end
I'm giving you an example how you can make your method serve you data when available. It's block based. So you don't have to consider asynchronous task here.
First define your completion block in your ServerRequest.h:
typedef void(^myCompletion)(NSDictionary*, NSError*);
And change your method's signature to this:
- (void) makePostRequest:(NSString *)params completion: (myCompletion)completionBlock;
Now change your method's implementation to something like this (I'm only posting your #try block, so just change your try block. Others remain same)
#try {
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error) {
NSLog(#"Error: %#", error);
if (completionBlock) {
completionBlock(nil, error);
}
}
if([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if(statusCode == [NetworkHelper HTTP_STATUS_CODE]) {
NSError *error;
self.responseContent = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:&error];
if (completionBlock) {
if (error == nil) {
completionBlock(self.responseContent, nil);
} else {
completionBlock(nil, error);
}
}
} else {
NSError *error;
self.errorContent = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
error:&error];
if (completionBlock) {
if (error == nil) {
completionBlock(self.errorContent, nil);
} else {
completionBlock(nil, error);
}
}
}
}
}];
[dataTask resume];
}
Finally, when you call this method from somewhere else, use this as:
[serverRequestObject makePostRequest:#"your string" completion:^(NSDictionary *dictionary, NSError *error) {
// when your data is available after NSURLSessionDataTask's job, you will get your data here
if (error != nil) {
// Handle your error
} else {
// Use your dictionary here
}
}];
I always used this solution when I needed to parse a feed JSON.
https://stackoverflow.com/a/20077594/2829111
But sendAsynchronousRequest is now deprecated and I'm stuck with this code
__block NSDictionary *json;
[[session dataTaskWithURL:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// handle response
json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"Async JSON: %#", json);
[collectionView reloadData];
}] resume];
And with this the reloadData argument takes a long time to execute. I've alredy tried forcing back to the main queue with:
__block NSDictionary *json;
[[session dataTaskWithURL:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// handle response
json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"Async JSON: %#", json);
dispatch_sync(dispatch_queue_create("com.foo.samplequeue", NULL), ^{[collectionView reloadData});
}] resume];
The problem is that the completion handler does not run on the main queue. But all UI updates must happen on the main queue. So dispatch that to the main queue:
[[session dataTaskWithURL:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// handle response
NSError *parseError;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
// do something with `json`
dispatch_async(dispatch_get_main_queue()), ^{[collectionView reloadData]});
}] resume];
Why don't you try JSONModel library....... it is so simple to use
-(void)getEmployeePerformance:(EmpPerformanceRequest*)request
withSuccesBlock:(succesEmployeePerformanceResponseBlock) successBlock
andFailBlock:(FailResponseBlock) failBlock
{
NSString* weatherUrl = [[ABWebServiceUtil sharedInstance]getEmployeePerformanceURL];
[HTTPClientUtil postDataToWS:weatherUrl parameters:[request toDictionary] WithHeaderDict:nil withBlock:^(AFHTTPRequestOperation *responseObj, NSError *error)
{
if(responseObj.response.statusCode == HTTP_RESPONSE_SUCESS)
{
EmpPerformanceArrayModel *empPerfArrModel;
if(responseObj.responseString)
{
empPerfArrModel = [[EmpPerformanceArrayModel alloc]initWithString:result error:nil];
empPerfArrModel.employeesArray = [empPerformanceModel arrayOfModelsFromDictionaries:empPerfArrModel.employeesArray];
}
if(successBlock)
{
successBlock(responseObj.response.statusCode, empPerfArrModel);
}
}else if (failBlock)
{
failBlock(responseObj.response.statusCode);
}
}];
}
for more detail follow this link...... it will brief you well
https://github.com/icanzilb/JSONModel
Try parsing JSON in connectionDidFinishLoading so you will get response as NSDictionary.
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
Class NSJSONSerializationclass = NSClassFromString(#"NSJSONSerialization");
NSDictionary *result;
NSError *error;
if (NSJSONSerializationclass)
{
result = [NSJSONSerialization JSONObjectWithData: responseData options: NSJSONReadingMutableContainers error: &error];
}
// If the webservice response having values we have to call the completionBlockā¦
if (result)
{
if (self.completionBlock != nil)
{
self.completionBlock(result);
}
}
}
I am trying to create a NSString function to return a string that has the NSURLSessionDataTask
- (NSString *) retrieveData
{
self.session = [NSURLSession sharedSession];
self.dataTask = [self.session dataTaskWithURL:[NSURL URLWithString:URL] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
if(data)
{
self.json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSLog(#"%#", self.json);
for(NSDictionary *stateArray in self.json)
{
NSString *sName = stateArray[#"State"];
if(self.state == sName)
{
NSString *sFlag = stateArray[#"State_Flag_Path"];
return sFlag;
}
}
}
else
{
NSLog(#"Failed to fetch URL: %#", error);
}
}];
[self.dataTask resume];
}
Then, I get an error message for incompatible block pointer types issue.
Can anyone please help?
Thank you in advance.
I found a way...
return the string after
[self.dataTask resume];
I have an iOS method that is now deprecated --NSURLConnection sendSynchronousRequest. This method worked and was fast.
I must be doing something wrong with the new method, as it is unacceptably slow.
The new method code I'm showing the whole routine is:
- (void)getData {
NSLog(#"%s", __FUNCTION__);
pathString = #"https://api.wm.com/json/jRoutes/.......";
NSURL *url = [NSURL URLWithString:pathString......];
NSURLSessionDataTask *downloadTask = [[NSURLSession sharedSession]
dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if ([response respondsToSelector:#selector(statusCode)]) {
if ([(NSHTTPURLResponse *) response statusCode] == 404) {
dispatch_async(dispatch_get_main_queue(), ^{
// alert
NSLog(#" NO DATA");
return;
});
}
}
// 4: Handle response here
[self processResponseUsingData:data];
}];
[downloadTask resume];
}
- (void)processResponseUsingData:(NSData*)data {
NSLog(#"%s", __FUNCTION__);
NSError *error = nil;
NSMutableDictionary* json = nil;
if(nil != data)
{
json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
}
if (error || !json)
{
NSLog(#"Could not parse loaded json with error:%#", error);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
allRoutesArray = [json valueForKey:#"Routes"];
NSLog(#"allRoutesArray count: %lu", (unsigned long)allRoutesArray.count);
[self.tableView reloadData];
});
}
}