I can't get data with help of NSURLSession - objective-c

I wrote some test code
-(void)downloadFile
{
[self getFile:^(id result) {
if([result isKindOfClass:[NSData class]])
{
self.myData=result;
}
}];
NSLog(#"%lu",(unsigned long)[self.myData length]);
}
-(void)getFile:(void(^)(id result))completionHandler
{
void(^partCompetionHandle)(NSURL *, NSURLResponse *, NSError *)=^(NSURL *url, NSURLResponse *response, NSError *error){
if(response==nil){
completionHandler(error);
return;
}
NSData *data=[NSData dataWithContentsOfURL:url];
completionHandler(data);
};
self.session=[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURL *url=[NSURL URLWithString:#"http://upload.wikimedia.org/wikipedia/commons/7/7f/Williams_River-27527.jpg"];
NSURLSessionDownloadTask *dataTask =[_session downloadTaskWithURL:url completionHandler:^(NSURL* data, NSURLResponse * response, NSError * error) {
partCompetionHandle(data, response, error);
}];
[dataTask resume];
}
My datas are zero, when I downloaded theirs
this line shows 0
NSLog(#"%lu",(unsigned long)[self.myData length]);

The classic : As the task is performed asynchronously, the logging of the result is much earlier than the data are downloaded.
Put the log line in the completion block
-(void)downloadFile
{
[self getFile:^(id result) {
if([result isKindOfClass:[NSData class]]) {
self.myData=result;
NSLog(#"%lu",(unsigned long)[self.myData length]);
}
}];
}

I changed code. But I don't see data
-(void)downloadFile
{
[self getFile:^(id result) {
if([result isKindOfClass:[NSData class]])
{
self.myData=result;
NSLog(#"%lu",(unsigned long)[self.myData length]);
}
}];
}
I called method downloadFile from main function
int main(int argc, const char * argv[]) {
#autoreleasepool {
DownloadFile *donload=[DownloadFile new];
[donload downloadFile];
}
return 0;
}
How can I get data?

Related

Extract Completions Handler Block into method using typdef

I would like to clean up my code some and extract a completion block to its own method. It's my understanding that I have to define a typdef with the same signature.
In the below example, I want to move the NSURLSessionUploadTask's on completion handler to its own method. I've looked at the Apple documentation and other SO questions, but I don't know if I am searching for the right thing.
typedef void (^PostCompletionHandler)(NSData *data,NSURLResponse *response,NSError *error);
#interface MyDemoClass() {
#property (copy, nonatomic) PostCompletionHandler completePostHandler;
#end
#implementation MyDemoClass
- (void) post {
NSData *postData = //data to pos;
NSMutableURLRequest * req = //create request;
NSURLSession * session = // create session;
NSURLSessionUploadTask *queryTask = [session uploadTaskWithRequest:req
fromData:postData
completionHandler:^(NSData *data,NSURLResponse *response,NSError *error) {
if (error) {
NSLog(#"Error: %#",error.localizedDescription);
} else {
NSDictionary * response = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(#"Post Response: %#",response.description);
}
}];
[queryTask resume];
}
- (void)someMethodThatTakesABlock:(PostCompletionHandler)blockName {
}
#end
This is what I would like to do is something like this:
NSURLSessionUploadTask *queryTask = [session uploadTaskWithRequest:req
fromData:postData
completionHandler:^(NSData *data,NSURLResponse *response,NSError *error) {
// What do I do here?
}];
- (void)someMethodThatTakesABlock:(PostCompletionHandler)blockName {
//how do I access the data, response and error parameters?
if (error) {
NSLog(#"Error: %#",error.localizedDescription);
} else {
NSDictionary * response = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(#"Post Response: %#",response.description);
}
}
The someMethodThatTakesABlock: has a single parameter, which is the block. The block is a reference to code that can be executed. It does not contain data.
If you want to separate out your completion handling to a separate method, then write a method
- (void)processCompletionWithData:(NSData*)data response:(NSURLResponse*)response error:(NSError*)error {
// Process it...
}
Then in the completion handler
NSURLSessionUploadTask *queryTask =
[session uploadTaskWithRequest:req
fromData:postData
completionHandler:^(NSData* data, NSURLResponse* response, NSError* error) {
[self processCompletionWithData:data response:response error:error];
}];

Asynchronously Api calls and returning data outside the block

I don't understand why I am getting null array outside the block code, even though I am using __block keyword on my array.
I am successfully getting data from a backend api with following code
`-(void)getJsonResponse:(NSString *)urlStr success:(void (^)(NSArray *responseDict))success failure:(void(^)(NSError* error))failure
{
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:urlStr];
// Asynchronously API is hit here
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// NSLog(#"%#",data);
if (error)
failure(error);
else {
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
// NSLog(#"%#",json);
success(json);
}
}];
[dataTask resume]; // Executed First
}`
Then in my function for returning the data I am using following
`- (NSArray *)get_data:(NSDictionary *)credentials{
NSString *urlStr =[ NSString stringWithFormat:#"http://test.com %#",credentials];
__block NSArray *jsonArray= [[NSArray alloc]init];
[self getJsonResponse:urlStr success:^(NSArray *responseArray) {
jsonArray = responseArray;
NSLog(#"%#",responseArray);
} failure:^(NSError *error) {
// error handling here ...
}];
NSLog(#"%#",jsonArray);
return jsonArray;
}
`
The issue here is although I am successfully getting data within getJsonResponse block, but when I am trying to return the response data array as function return I am getting null for jsonArray. I thought assigning __block infront of jsonArray should retain the data assign within the block code ?
The second approach is not to use Async way like following
`- (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
{
NSError __block *err = NULL;
NSData __block *data;
BOOL __block reqProcessed = false;
NSURLResponse __block *resp;
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable _data, NSURLResponse * _Nullable _response, NSError * _Nullable _error) {
resp = _response;
err = _error;
data = _data;
reqProcessed = true;
}] resume];
while (!reqProcessed) {
[NSThread sleepForTimeInterval:0];
}
*response = resp;
*error = err;
return data;
}`
That way its blocking the main thread whilst waiting for data.
I would suggest using the same approach of getJsonResponse for your get_data function:
- (void)get_data:(NSDictionary *)credentials finish:(void(^)(NSArray *data))finish{
NSString *urlStr =[ NSString stringWithFormat:#"http://test.com %#",credentials];
__block NSArray *jsonArray= [[NSArray alloc]init];
[self getJsonResponse:urlStr success:^(NSArray *responseArray) {
jsonArray = responseArray;
if (finish) {
finish(jsonArray);
}
} failure:^(NSError *error) {
// error handling here ...
}];
}

Facing an issue with NSURLSessionDataTask with SynchronousRequest in objective-c

Here is my working code with NSURLConnection sendSynchronousRequest :
+ (Inc*)getData:(NSString*)inUUID {
NSString* urlString = [NSString stringWithFormat:#"/inc/%#", incUUID];
NSURLRequest* request = [[HttpRequest requestWithRelativePath:urlString] toNSMutableURLRequest];
NSDictionary* json = [self getJSONForRequest:request];
return [Inc incFromDictionary:json];
}
+ (NSDictionary*)getJSONForRequest:(NSURLRequest*)request {
NSData* responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
return [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
}
But, sendSynchronousRequest:request is deprecated.
So that, I used NSURLSessionDataTaskinstead of sendSynchronousRequest. Here, is the code which I implemented:
+ (Inc*)getData1:(NSString*)inUUID {
NSString* urlString = [NSString stringWithFormat:#"/in/%#", inUUID];
NSURLRequest* request = [[HttpRequest requestWithRelativePath:urlString] toNSMutableURLRequest];
//NSDictionary* json = [self getJSONForRequest1:request];
__block NSDictionary* json;
dispatch_async(dispatch_get_main_queue(), ^{
[self getJsonResponse1:request success:^(NSDictionary *responseDict) {
json = [responseDict valueForKeyPath:#"detail"];;
//return [Inc incFromDictionary:json];
} failure:^(NSError *error) {
// error handling here ...
}];
});
return [Inc incFromDictionary:json];
}
+ (void)getJsonResponse1:(NSURLRequest *)urlStr success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure
{
NSURLSessionDataTask *dataTask1 = [[NSURLSession sharedSession] dataTaskWithRequest:urlStr completionHandler:^(NSData *data, NSURLResponse *response,
NSError *error) {
NSLog(#"%#",data);
if (error)
failure(error);
else {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#",json);
success(json);
}
}];
[dataTask1 resume]; // Executed First
}
The problem is return statement call in getData1 before finish the api call.
Thanks in advance.
As mentioned in the comments you need a completion handler.
Something like this (untested):
+ (void)getData1:(NSString*)inUUID success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure {
NSString* urlString = [NSString stringWithFormat:#"/in/%#", inUUID];
NSURLRequest* request = [[HttpRequest requestWithRelativePath:urlString] toNSMutableURLRequest];
[self getJsonResponse1:request success:^(NSDictionary *responseDict) {
NSDictionary* json = [responseDict valueForKeyPath:#"detail"];
success(json);
} failure:^(NSError *error) {
failure(error);
}];
}
+ (void)getJsonResponse1:(NSURLRequest *)urlStr success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure
{
NSURLSessionDataTask *dataTask1 = [[NSURLSession sharedSession] dataTaskWithRequest:urlStr completionHandler:^(NSData *data, NSURLResponse *response,
NSError *error) {
NSLog(#"%#",data);
if (error)
failure(error);
else {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#",json);
success(json);
}
}];
[dataTask1 resume]; // Executed First
}
And to call
[MyClass getData1:#"asdf" success:^(NSDictionary *responseDict) {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *json = [responseDict valueForKeyPath:#"detail"];
Inc *inc = [Inc incFromDictionary:json];
// do something witrh `inc`
});
} failure:^(NSError *error) {
// error handling here ...
}];
Consider to use instances and instance methods of your class(es) rather than only class methods.

Recursive pagination of Instagram feed. How can I do this faster?

I'm working with Instagram API and want to show a screen with user's photos ordered by number of likes.
So, I need to get an array with all photos.
There is how I do this:
- (void)fetchAllPostsFromURL:(NSURL *)url success:(void(^)(NSArray *posts))success {
__block NSArray *allPosts = [NSMutableArray array];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
if (!error) {
NSError *jsonError = nil;
id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if ([result isKindOfClass:[NSDictionary class]] && !jsonError) {
NSMutableArray *tempArray = [NSMutableArray array];
for (NSDictionary *jsonPost in [result objectForKey:#"data"]) {
DVVPost *post = [[DVVPost alloc] initWithJSONData:jsonPost];
[tempArray addObject:post];
}
allPosts = [NSArray arrayWithArray:tempArray];
}
NSURL *paginationURL = [self getPaginationURLFromResult:result];
if (paginationURL) {
[self fetchAllPostsFromURL:paginationURL success:^(NSArray *posts) {
allPosts = [allPosts arrayByAddingObjectsFromArray:posts];
success(allPosts);
}];
} else {
success(allPosts);
}
} else {
NSLog(#"error: %#", [error localizedDescription]);
}
}];
[dataTask resume];
}
It works, but it takes pretty much amount of time. As I see, there is 2-3 requests per second, each gives me 20 photos.
How can I make that faster?
API: https://instagram.com/developer/endpoints/users/

NSURLConnection Asyncronous Request returns null

Followed a recently made YouTube tutorial try connecting to web address asynchronously.
Retuning null for the response, but the data length is greater than 0. I've seen other code that does both null & length checking, which I didn't throw, just NSLog'd them.
#implementation ViewController
-(void)fetchAddress:(NSString *)address {
NSLog(#"Loading Address: %#", address);
[iOSRequest requestToPath:address onCompletion:^(NSString *result, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
[self stopFetching:#"Failed to Fetch"];
NSLog(#"%#", error);
} else {
[self stopFetching:result];
NSLog(#"Good fetch: %#", result);
}
});
}];
}
- (IBAction)fetch:(id)sender {
[self startFetching];
[self fetchAddress:self.addressField.text];
NSLog(#"Address: %#", self.addressField.text);
}
-(void)startFetching {
NSLog(#"Fetching...");
[self.addressField resignFirstResponder];
[self.loading startAnimating];
self.fetchButton.enabled = NO;
}
-(void)stopFetching:(NSString *)result {
NSLog(#"Done Fetching %#", result);
self.outputLabel.text = result;
[self.loading stopAnimating];
self.fetchButton.enabled = YES;
}
#implementation iOSRequest
+(void)requestToPath:(NSString *)
path onCompletion:(RequestCompletionHandler)complete {
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]
cachePolicy:NSURLCacheStorageAllowedInMemoryOnly
timeoutInterval:10];
NSLog(#"path: %# %#", path, request);
[NSURLConnection sendAsynchronousRequest:request
queue:backgroundQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSString *result = [[NSString alloc] initWithData:
data encoding:NSUTF8StringEncoding];
if (complete) {
complete(result, error);
NSLog(#"result: %# %lu %#", result, (unsigned long)[data length], error);
}
}];
}
request is http://www.google.com>
response is null
data length is 13644
Not sure what is wrong... any suggestions?
Thanks to Josh Caswell for pointing in the right direction, but allowing me to figure it out myself.
Changed the initWithData encoding from NSUTF8StringEncoding to NSASCIIStringEncoding.
[NSURLConnection sendAsynchronousRequest:request
queue:backgroundQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSString *result = [[NSString alloc] initWithData:
data encoding:NSASCIIStringEncoding];
if (complete) {
complete(result, error);
NSLog(#"result: %# %lu %#", result, (unsigned long)[data length], error);
}
}];