I have declared method as:
- (void) authorizeUser:(OauthObject *) user withUsername: (NSString *) username withPassword: (NSString *) password completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error)) CallCompletion;
I try to call this method like as:
[[ManagerServerRequest sharedManagerServerRequest] authorizeUser:(OauthObject *) withUsername:self.login.text withPassword:self.login.text completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// TODO
}]
Where ManagerServerRequest sharedManagerServerRequest is singlton object.
I get error:
use undeclared identifier WithUsername
How to call this method correctly?
Review:
I pass oauthObj:
OauthObject* oauthObj = [[OauthObject alloc] init];
[[ManagerServerRequest sharedManagerServerRequest]
authorizeUser: oauthObj and:
withUsername:self.login.text
withPassword:self.login.text
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// TODO
}]
I can error
The problem is unrelated to block syntax. In your code
[[ManagerServerRequest sharedManagerServerRequest]
authorizeUser:(OauthObject *)
withUsername:self.login.text
withPassword:self.login.text
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// TODO
}]
You are missing the actual argument for authorizeUser:, replace (OauthObject *) with the actual object and you are fine.
The code doesn't compile, but that's what you said in your question anyway :-)
From the error message, it looks like you are using WithUsername when you should be using withUsername.
Related
I have a method, the method have return the nsdata value, but I don't known how to get the return value from NSURLSessionDataTask block. and how to call the getDownloadFileData methods.Code for task is :-
caller:
NSData *getFileDataResult = [self getDownloadFileData:pathString];
method:
- (NSData*) getDownloadFileData : (NSString*) filePath {
NSURLSessionDataTask *downloadFile = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:filePath] completionHandler:^(NSData *fileData, NSURLResponse *response, NSError *error){
// .....
// fileData should return out.
[downloadFile resume];
});
// I want to return the fileData after download completion.
// how to return?
}
Have anyone can give me a hand?
Thank you very much.
Please check my answer, I hope this helpful
- (NSData *)getDownloadFileData:(NSString *)filePath {
__block NSData *responseData = nil;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
NSURLSessionDataTask *downloadFile = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:filePath] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
responseData = data;
dispatch_semaphore_signal(sema);
}];
[downloadFile resume];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
return responseData;
}
- (void)whereToCall {
// Because to prevent the semaphore blocks main thread
dispatch_queue_t myQueue = dispatch_queue_create("com.abc", 0);
dispatch_async(myQueue, ^{
NSData *data = [self getDownloadFileData:#"urlString"];
});
}
- (void)betterGetDownloadFileData:(NSString *)filePath completion:(void (^)(NSData * __nullable data))completion {
NSURLSessionDataTask *downloadFile = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:filePath] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (completion) {
completion(data);
}
}];
[downloadFile resume];
}
I recommend you should design your code as my suggestion that using block instead.
First of all you have put resume method at wrong place. It should be like this:
- (NSData*) getDownloadFileData : (NSString*) filePath {
NSURLSessionDataTask *downloadFile = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:filePath] completionHandler:^(NSData *fileData, NSURLResponse *response, NSError *error){
// .....
// fileData should return out.
});
[downloadFile resume];//NOTICE THE PLACEMENT HERE
// I want to return the fileData after download completion.
// how to return?
}
Second thing is , you can simply create a NSData variable and assign it the value in completion block rather than passing data back.
OR
Simply do like this in completion block
if(fileData){
return fileData;
}
I had iOS framework which it send JSON to server using NSURLSessionDataTask like this :
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int responseStatusCode = [httpResponse statusCode];
if (responseStatusCode == 200)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate onJsonHttpResult:data andStatusResponse:responseStatusCode];
});
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate onJsonHttpResult:nil andStatusResponse:responseStatusCode];
});
}
}];
[postDataTask resume];
but whenever I run it, [self.delegate onJsonHttpResult:nil andStatusResponse:responseStatusCode]; not called.
is there any way to get value outside NSURLSessionDataTask when it run inside the framework ?
Thanks
My suggestion is to create and use APIHelperClass with completionBlock.
That will be more easy and affective then use of custom Delegate as per my view.
To create it you can do as follow:
In APIHelperClass.h
#import <Foundation/Foundation.h>
#interface APIHelperClass : NSObject
+(void)apiCallSharedSessionPOST:(NSURLRequest *)request withCompletionHandlar:(void (^) (NSDictionary *dicResult,NSError *error, int status))completionBlock;
#end
And
APIHelperClass.m
#import "APIHelperClass.h"
#implementation APIHelperClass
+(void)apiCallSharedSessionPOST:(NSURLRequest *)request withCompletionHandlar:(void (^) (NSDictionary *dicResult,NSError *error, int status))completionBlock;
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int responseStatusCode = (int)[httpResponse statusCode];
if (error!=nil)
{
completionBlock(nil,error,responseStatusCode);
[task suspend];
}
else
{
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
completionBlock(dic,error,responseStatusCode);
[task suspend];
}
}];
[task resume];
}
Then you can use that class for whole App and you don't need to create custom Delegates always.
Then Use that where you want Like :
NSURLRequest *request;
/*
Configure your Request Here
*/
[APIHelperClass apiCallSharedSessionPOST:request withCompletionHandlar:^(NSDictionary *dicResult, NSError *error, int status) {
}];
Thanks for help but I still need delegate to return my value outside framework.
I got issued that whenever I done with NSURLSessionDataTask, delegate is became null, I think it cause that delegate already released after I got response from NSURLSessionDataTask, So I tried to change #property delegate to strong and it work. I can return my value using delegate again. Thanks
I'm using blocks to get header fields from response in one class and I have to get that in another class.
I implemented code like this
In first class:
- (void)viewDidLoad {
[super viewDidLoad];
UserAuthentication *auth = [[UserAuthentication alloc]init];
NSDictionary *dict = [auth getUserConfiguration];
NSLog(#"%#",dict);
}
In userAuthentication class:
-(NSDictionary *)getUserConfiguration;
{
__block NSDictionary *resultDictionary;
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:#"http://72.52.65.142:8083/auth"]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if ([response respondsToSelector:#selector(allHeaderFields)]) {
resultDictionary = [httpResponse allHeaderFields];
NSLog(#"%#",resultDictionary);
}
}] resume];
NSLog(#"%#",resultDictionary);
return resultDictionary;
}
Here my problem is in first class I'm getting dict as null.
Even in userAuthentication class also I'm getting null.
But after some time call back method is calling and then I can see the response correctly in completionHandler.
So how I can get response in firstClass?
You are misunderstanding the basic principle of async operation that runs in background thread and when the operation is completed it gives you data in completion block.
To get response in viewDidLoad Method of second class you need to use blocks. like below
-(void)getUserConfigurationOnCompletion:(void (^)(NSDictionary *))completion
{
__block NSDictionary *resultDictionary;
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:#"http://72.52.65.142:8083/auth"]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if ([response respondsToSelector:#selector(allHeaderFields)]) {
resultDictionary = [httpResponse allHeaderFields];
// Call completion with parameter
completion(resultDictionary);
}
}] resume];
}
and use it like this in viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
UserAuthentication *auth = [[UserAuthentication alloc]init];
[auth getUserConfigurationOnCompletion:^(NSDictionary *dict){
// do necessary work with response dictionary here
NSLog(#"%#",dict);
}];
}
That's something you'll have to get used to: Anything that is related to internet access (and some things not related to it) cannot be returned immediately - unless you are willing to wait for it, block your user interface, and make your users very, very unhappy.
You have to write your application in such a way that it can be in four states: Never asked for the user configuration, asking for the user configuration, having asked for and received the user configuration, or having asked for the user configuration and failed. In this case your view must handle all four possibilities and must handle when the situation changes.
You are using NSURLSession! It performs tasks on a background thread!
Completion block is called only when you get the response from the server. Naturally it will take time to complete the request. You should use blocks to complete the request and return the result on completion.
-(void)getUserConfigurationAndOnCompletion:(void(ˆ)(NSDictionary *dict, NSError *error))completion;
{
__block NSDictionary *resultDictionary;
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:#"http://72.52.65.142:8083/auth"]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if ([response respondsToSelector:#selector(allHeaderFields)]) {
resultDictionary = [httpResponse allHeaderFields];
NSLog(#"%#",resultDictionary);
//This will call the block in the first class with the result dictionary
dispatch_async(dispatch_get_main_queue(), ^{
if(!error){
completion(resultDictionary,nil);
}else{
completion(nil,error);
}
});
}] resume];
}
When you call the above code from your first class, it will create a block there and you will get the required dictionary over there in the block parameter!
Your method should be like,
-(void)getUserConfigurationwithCompletionHandler : (void (^)(NSDictionary* resultDictionary))completionHandler
{
__block NSDictionary *resultDictionary;
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:#"http://72.52.65.142:8083/auth"]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if ([response respondsToSelector:#selector(allHeaderFields)]) {
resultDictionary = [httpResponse allHeaderFields];
NSLog(#"%#",resultDictionary);
completionHandler(resultDictionary);
}
}] resume];
NSLog(#"%#",resultDictionary);
}
and you can access it like,
- (void)viewDidLoad {
[super viewDidLoad];
[self getUserConfigurationwithCompletionHandler:^(NSDictionary *resultDictionary) {
// you can acess result dictionary here
NSLog(#"%#",resultDictionary);
}];
}
because you will getting data in response of webservice(from server) so it takes some time to complete so you have to return data from completion handler of webservice call and you can't return data from completion handler so you have to create own completion handler and call as i have mentioned above. you can access resultDictionary in completionHandler and you can show new VC from this completionHandler.
You have to call a method in your first class in your completionHandler.
Create a property of type YOURFIRSTCLASS *myfirstclass in your UserAuthentication Class.
Pass your firstclass with "self" to the UserAuthentication object.
create visible method in your firstclass "-(void)responseCaller:(NSDictionary)dict"
call the method in your response method
YOURFIRSTCLASS .h:
-(void)responseCaller:(NSDictionary)dict;
YOURFIRSTCLASS .m
-(void)responseCaller:(NSDictionary)dict
{NSLog(#"%#",dict);}
- (void)viewDidLoad {
[super viewDidLoad];
UserAuthentication *auth = [[UserAuthentication alloc]init];
auth.myfirstclass = self;
NSDictionary *dict = [auth getUserConfiguration];
NSLog(#"%#",dict);
}
UserAuthentication .h
#import "YOURFIRSTCLASS.h"
#property (nonatomic) *myfirstclass;
UserAuthentication .m
-(NSDictionary *)getUserConfiguration;
{
__block NSDictionary *resultDictionary;
NSURLSession *session = [NSURLSession sharedSession];
__weak myfirstclassSave = myfirstclass;
[[session dataTaskWithURL:[NSURL URLWithString:#"http://72.52.65.142:8083/auth"]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if ([response respondsToSelector:#selector(allHeaderFields)]) {
resultDictionary = [httpResponse allHeaderFields];
[myfirstclassSave responseCaller:resultDictionary ];
}
}] resume];
return resultDictionary;
}
Something like that
I have an iOS app with a function which is in charge of making an asynchronous network request. The request itself works just fine, but the problem I am having is with the function return statement which is causing errors.
Here is my function:
-(NSArray *)get_data:(NSString *)size {
// Set up the data request.
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://mywebsite.com/info.json"]];
NSURLRequest *url_request = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// Begin the asynchronous data loading.
[NSURLConnection sendAsynchronousRequest:url_request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error == nil) {
// Convert the response JSON data to a dictionary object.
NSError *my_error = nil;
NSDictionary *feed = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&my_error];
if (feed != nil) {
// Store the returned data in the data array.
NSArray *topping_data;
for (int loop = 0; loop < [[feed objectForKey:#"toppings_data"] count]; loop++) {
NSString *size_name = [NSString stringWithFormat:#"%#", [[[feed objectForKey:#"toppings_data"] objectAtIndex:loop] valueForKey:#"Size"]];
if ([size_name isEqualToString:size]) {
topping_data = [[feed objectForKey:#"toppings_data"] objectAtIndex:loop];
}
}
return topping_data;
}
else {
return #[#"no data"];
}
}
else {
return #[#"no data"];
}
}];
}
I am getting the following error message on the line of code [NSURLConnection sendAsync....:
Incompatible block pointer types sending 'NSArray *(^)(NSURLResponse
*__strong, NSData *__strong, NSError *__strong)' to parameter of type 'void (^ _Nonnull)(NSURLResponse * _Nullable __strong, NSData *
_Nullable __strong, NSError * _Nullable __strong)'
What am I doing wrong here?
All I am trying to avoid, is the function returning BEFORE the asynchronous request has completed. Otherwise the function will not return any data, which is not what I want.
Thanks for your time, Dan.
best way to return data in async block is make a block callback as argument of function and callback return value here:
- (void)get_data:(NSString *)size completionHandler:(void (^)(NSArray *array))completionHandler {
// ...
[NSURLConnection sendAsynchronousRequest:url_request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// ...
completionHandler(array);
// ...
}];
}
use :
[self get_data:someString completionHandler:^(NSArray *array) {
// process array here
}];
The block returns nothing:
void ^(NSURLResponse *, NSData *, NSError *)
So you cannot return things:
return #[#"no data"];
The code that calls the block is not interested in what it returns; if you want to store state then add an instance variable or call a method.
Change
[NSURLConnection sendAsynchronousRequest:url_request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
to
[NSURLConnection sendAsynchronousRequest:url_request queue:queue completionHandler:^(NSURLResponse *_Nullable response, NSData *_Nullable data, NSError *_Nullable error)
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
After creating an NSURLSession, it appears that making multiple web queries with NSURLSessionDataTask can lead to some code writing redundancy, which I'd like to clean up. The following code is repeated multiple times:
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}];
[task resume];
Could the task be initiated from inside a dedicated function? It would be easy to pass in the NSMutableURLRequest as a variable but what about the completion handler?
Theoretically you could pass in the completion handler as a block, right? The problem I ran into is that the callback variables (data & response) can't be referred to from outside the provided completion handler, so how can you write a block that refers to them?
Right now I have 3 or 4 separately written tasks going, and it looks like it may stay that way unless someone has an idea!
EDIT, SOLUTION:
The method definition is:
- (void) engage:(NSMutableURLRequest *)request with:(void (^)(NSData *, NSURLResponse *, NSError *))yourmom;
& The block literal that it takes looks like:
void (^yourmom)(NSData *, NSURLResponse *, NSError *) = ^(NSData *data, NSURLResponse *response, NSError *error) {
// stuff
};
Basically, the key thing I was missing was that I needed my block to accept variables so that I could refer to them by name without compiler errors. The above code creates a variable-accepting block which lets me get around the compiler warnings about out of scope variables. Also if the code
[self engage:request with:yourmom];
shows up somewhere we'll know where it came from.
If you're asking how can you pass the completion block to the utility method, you should just supply a block parameter to your method:
- (NSURLSessionDataTask *)startDataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))block
{
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:block];
[task resume];
return task;
}
Or, if there are portions of the completion block which you're repeating all the time, then go ahead and put that in your utility method, but then invoke the caller's block:
- (NSURLSessionDataTask *)startDataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))block
{
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
// do here stuff that I'll always do, such as logging errors, checking response codes, etc.
// when that's done, I can now invoke the caller's block
if (block) {
block(data, response, error);
}
}];
[task resume];
return task;
}
I'd say make a category on NSURLSession and do the following:
First typedef a completion block in the header file:
typedef void (^NSURLSessionDataTaskCompletionHandler) (NSData *data, NSURLResponse *response, NSError *error);
Then add a method to the header file:
- (NSURLSessionDataTask *)startTaskWithRequest:(NSURLRequest *)request completionHandler:(NSURLSessionDataTaskCompletionHandler)completionHandler;
Finally the body of the function in the implementation file:
- (NSURLSessionDataTask *)startTaskWithRequest:(NSURLRequest *)request completionHandler:(NSURLsessionDataTaskCompletionHandler)completionHandler
{
NSURLSessionDataTask *task = [self dataTaskWithRequest:request completionHandler:completionHandler];
[task resume];
return task;
}
Now you get to very easily do something like:
[session startTaskWithRequest:request completionHandler:(NSData *data, NSURLResponse *response, NSError *error){
// do stuff
}];