Object-C/iOS :How to Use Asynchronous to Get a Data from Url

Object-c/iOS :How to use ASynchronous to get a data from URL?

Short answer: You can use

+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;

See NSURLConnectionDelegate for the informal delegate protocol (all methods are optional)

Long answer:

Downloading data asynchronously is not as straightforward as the synchronous method. First you have to create your own data container e.g. a file container

//under documents folder/temp.xml
file = [[SomeUtils getDocumentsDirectory] stringByAppendingPathComponent:@"temp.xml"]
NSFileManager *fileManager = [NSFileManager defaultManager];
if(![fileManager fileExistsAtPath:file]) {
[fileManager createFileAtPath:file contents:nil attributes:nil];
}

When you connect to server:

[NSURLConnection connectionWithRequest:myRequest delegate:self];

You have to fill the container with the data you receive asynchronously:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:file];
[fileHandle seekToEndOfFile];
[fileHandle writeData:data];
[fileHandle closeFile];
}

You have to manage errors encountered using:

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 

If you want to capture the server response:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response

Handle when connection finished loading:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection

How to work Synchronous and Asynchronous web in obj c

Two separate issues here.

  1. First, the request should be asynchronous and use NSURLSession. For example:

    NSURL *url = [NSURL URLWithString:@"http://content.guardianapis.com/search?api-key=test"];
    NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (error) {
    NSLog(@"%@", error);
    return;
    }

    NSError *parseError;
    NSDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];

    if (responseObject) {
    NSLog(@"%@", responseObject);
    dispatch_async(dispatch_get_main_queue(), {
    // if you want to update your UI or any model objects,
    // do that here, dispatched back to the main queue.
    });
    } else {
    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"problem parsing response, it was:\n%@", string);
    }
    }];
    [task resume];

    // but do not try to use `data` here, as the above runs asynchronously
    // do everything contingent upon this request _inside_ the completion
    // handler above

    Note, when you get the JSON response, you can parse that with NSJSONSerialization, as shown above.

  2. Nowadays, using http for requests is frowned upon because it's not secure. You generally should use https. And NSURLSession will enforce this, returning an error if you try to use http. But if you want to force it to permit an insecure http request, you have to update your info.plist with an entry that looks like:

    <key>NSAppTransportSecurity</key>
    <dict>
    <key>NSExceptionDomains</key>
    <dict>
    <key>guardianapis.com</key>
    <dict>
    <!--Include to allow subdomains-->
    <key>NSIncludesSubdomains</key>
    <true/>
    <!--Include to allow HTTP requests-->
    <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
    <true/>
    <!--Include to specify minimum TLS version-->
    <key>NSTemporaryExceptionMinimumTLSVersion</key>
    <string>TLSv1.1</string>
    </dict>
    </dict>
    </dict>

    So, go to your info.plist, control-click on it and choose "Open As" ... "Source Code" and then add the above into it. See https://stackoverflow.com/a/31254874/1271826 for more information.

Asynch download from URL iPhone

Below is my code for async downloading images and data. You can play with it for your aims.

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSLog(@"Screen %@ - pauseBannerFileImage download starts", self.name);
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:newUrlForImage]]];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:newUrlForImage]];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"!-Screen %@-!pauseBannerFileImage downloaded", self.name);
self.pauseBannerFileImage = image;
});
});

Asynchronous request example

There are couple of things you could do.

  1. You can use sendAsynchronousRequest and handle the callback block.
  2. You can use AFNetworking library, which handles all your requests in asynchronous fashion. Very easy to use and set up.

Code for option 1:

NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
//NSLog(@"Error,%@", [error localizedDescription]);
}
else {
//NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}
}];

Code for option 2:

You might want to download the library & include it in your project first. Then do the following. You can follow the post on setting up here

NSURL *url = [NSURL URLWithString:@"http://httpbin.org/ip"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];

AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(@"IP Address: %@", [JSON valueForKeyPath:@"origin"]);
} failure:nil];

[operation start];

iPhone use of mutexes with asynchronous URL requests

If it's possible that any data (including classes) will be accessed from two threads simultaneously you must take steps to keep these synchronized.

Fortunately Objective-C makes it ridiculously easy to do this using the synchronized keyword. This keywords takes as an argument any Objective-C object. Any other threads that specify the same object in a synchronized section will halt until the first finishes.

-(void) doSomethingWith:(NSArray*)someArray
{
// the synchronized keyword prevents two threads ever using the same variable
@synchronized(someArray)
{
// modify array
}
}

If you need to protect more than just one variable you should consider using a semaphore that represents access to that set of data.

// Get the semaphore.
id groupSemaphore = [Group semaphore];

@synchronized(groupSemaphore)
{
// Critical group code.
}

How to return data gotten from a web service in objective- c (iPhone)?

There are really two ways to go about this.

  1. Create a delegate interface
  2. Use Blocks

I would strongly advise against using the synchronous methods - unless you are/have created your own asynchronous framework around them (i.e. you are manually starting another thread and executing your synchronous request on that thread). In the long run you will realize you need the requests to be async, and you'll have to re-work everything such that they are.

To give a quick overview of the two options I gave:

1. Create a delegate interface

The idea here is to create a class which performs the request, and create a protocol the caller must implement. When the request is complete, you will invoke a specified method on the delegate with the data:

The protocol might look something like this:

@protocol RequestClassDelegate <NSObject>

- (void)requestCompleted:(ResponseClass *)data;
- (void)requestError:(NSError *)error;

@end

The class which makes the request might look something like this:

@interface RequestClass : NSObject

- (void)makeRequest:(id<RequestClassDelegate>)delegate;

@end

And the request class implementation might contain some of the following, in addition to your connection logic:

@implementation RequestClass
{
__weak id<RequestClassDelegate> _delegate;
}

// Connection Logic, etc.

- (void)makeRequest:(id<RequestClassDelegate>)delegate
{
_delegate = delegate;
// Initiate the request...
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

// Processing, etc.

// Here we'll call the delegate with the result:
[_delegate requestCompleted:theResult];
}

@end

2. Use Blocks

This solution is much the same as the first solution - but, a bit more elegant in my opinion. Here, we'll change the RequestClass to use blocks instead of a delegate:

typedef void (^requestCompletedBlock)(id);
typedef void (^requestErrorBlock)(NSError *);
@interface RequestClass : NSObject

@property (nonatomic, copy) requestCompletedBlock completed;
@property (nonatomic, copy) requestErrorBlock errored;

- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error;

@end

And the implementation of that might look something like this:

@implementation RequestClass

@synthesize completed = _completed;
@synthesize errored = _errored;

// Connection Logic, etc.

- (void)makeRequest:(requestCompletedBlock)completed error:(requestErrorBlock)error
{
self.completed = completed;
self.errored = error;
// Initiate the request...
}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];

// Processing, etc.

// Here we'll call the delegate with the result:
self.completed(theResult);
}

@end


Related Topics



Leave a reply



Submit