I am getting JSON data from a server. Since data returned may be used for different purposes, different processing methods are required.
Below is my expected function:
- (void)getFrom:(NSString *)server method:(SEL)processingMethod {
NSURL *url = [NSURL URLWithString:server];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:#"GET"];
[req setValue:#"application/x-www-form-urlencoded charset=utf-8"
forHTTPHeaderField:#"Content-Type"];
[NSURLConnection sendAsynchronousRequest:req
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"error requesting: %#", error.localizedDescription);
return;
}
NSLog(#"data: %#", [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]); // print JSON
// How to call appropriate method (passed from parameter) here??
[self passJSON:data toMethod:processingMethod];
}];
}
- (void)passJSON:(NSData *)data toMethod:(SEL)method {
if ([self respondsToSelector:method]) {
[self performSelector:#selector(method) // WARNING: Undeclared selector method
withObject:data];
}
}
- (void)processData1:(NSData *)JSON {
// process data1
}
- (void)processData2:(NSData *)JSON {
// process data2
}
What would I do to call my function - (void)getFrom:(NSString *)server method:(SEL)processingMethod having any processingMethod of my interest passed in, such as:
- (void)somefunction {
[self getFrom:#"http://www.datasite.com/data1" method:#selector(processData1:)]; // after getting data1, processData1 is called having JSON data1 passed in
[self getFrom:#"http://www.datasite.com/data2" method:#selector(processData2:)]; // after getting data2, processData2 is called having JSON data2 passed in
}
Then I found this link (especially wbyoung's reply): performselector may cause a leak
But I could not figure out how to pass a parameter, in this case: JSON data, to someMethod
Any suggestion?
Thanks,
Thank to Scott's reply in the link above perform selector may cause a leak
I put some pragma marks around [self performSelector:method withObject:data] to silent the warning, and all work well.
Below is my complete code:
- (void)getFrom:(NSString *)server method:(SEL)processingMethod {
NSURL *url = [NSURL URLWithString:server];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod:#"GET"];
[req setValue:#"application/x-www-form-urlencoded charset=utf-8"
forHTTPHeaderField:#"Content-Type"];
[NSURLConnection sendAsynchronousRequest:req
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"error requesting: %#", error.localizedDescription);
return;
}
NSLog(#"data: %#", [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding]); // print JSON
// How to call appropriate method (passed from parameter) here??
[self passJSON:data toMethod:processingMethod];
}];
}
- (void)passJSON:(NSData *)data toMethod:(SEL)method {
if ([self respondsToSelector:method]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector:method
withObject:data];
#pragma clang diagnostic pop
}
}
- (void)processData1:(NSData *)JSON {
// process data1
}
- (void)processData2:(NSData *)JSON {
// process data2
}
Usage:
- (void)somefunction {
[self getFrom:#"http://www.datasite.com/data1" method:#selector(processData1:)]; // after getting data1, processData1 is called having JSON data1 passed in
[self getFrom:#"http://www.datasite.com/data2" method:#selector(processData2:)]; // after getting data2, processData2 is called having JSON data2 passed in
}
However, this approach is just to suppress the warning, not actually/completely solve the problem by coding in a right way. Any more suggestions, ideas are welcome!
Regards,
Related
I have the following code in my app, and there is a memory leak with the 'dict' object. So I have a few questions about the best practice for this after the code:
// Convert JSON to dict
NSError *error = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
// If error return nil
if (error)
completion(nil, [self handleSerializationError:error]);
else if (![UsefulFunctions objectContainsData:dict[#"data"]])
completion(nil, NO);
// No error then return dict
else
completion(dict, NO);
});
About the code: The completion handler passes back the dict object which is then used to create core data entities (hence the main thread) based upon the calling function. The data being serialised is from an NSURLConnection. So the questions are as follows:
1) Is this the correct practice for passing back data in a completion handler?
2) Which function should take care of the memory management, should it be the calling class?
3) Is it worth wrapping this in an auto-release pool, or is that not how they are supposed to be used (kind of a separate question).
4) Can anyone see any obvious reasons from this function alone why the dict is retained, or is it purely down to the calling class?
Thanks for any help
EDIT (Whole Function), just to confirm I am using ARC and this function is called from the main thread:
- (void)downloadJSONFromURL:(NSURL *)url withCompletionHandler:(void (^)(id object, BOOL retry))completion
{
// Check URL
if (url)
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSString *authToken = [NSString stringWithFormat:#"token %#", [UsefulFunctions returnActiveAPIKey]];
[request setValue:authToken forHTTPHeaderField:#"Authorization"];
[request setHTTPMethod:#"GET"];
// Create an asynch request, don't want to hold up main queue
[NSURLConnection sendAsynchronousRequest:request queue:[self operationQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
// If data exists
if (data)
{
// Convert JSON to dict
NSError *error = nil;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
// If error return nil
if (error)
completion(nil, [self handleSerializationError:error]);
else if (![UsefulFunctions objectContainsData:dict[#"data"]])
completion(nil, NO);
// No error then return dict
else
completion(dict, NO);
});
}
// If error
else if (connectionError)
{
//NSLog(#"Connection Error: %#, Code: %lu", connectionError.description, (long)connectionError.code);
dispatch_async(dispatch_get_main_queue(), ^{
// Return nil
completion(nil, [self handleConnectionError:connectionError]);
});
}
}];
}
}
I have code written that uploads an image to the server, but I am not sure how to retrieve the images after I upload them. I tried using an NSURL request, but the did receiveData delegate method is never called. Below I've included all relevant code related to uploading the picture, and then my attempt at pulling the data using an NSURL request. Is there anything conceptually that I'm doing wrong? Thank you.
- (IBAction)nextButtonPressed:(id)sender {
[self.signupController uploadProfilePicture:UIImagePNGRepresentation(self.imageView.image) completion:^(NSError *error){
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[[SyncController sharedInstance] sync];
[self performSegueWithIdentifier:#"addFriendsSegue" sender:self];
}];
}];
}
And the uploadProfilePicture method:
- (void)uploadProfilePicture:(NSData *)imageData completion:(void (^)(NSError *error))completion {
BRUser *user = [BRSession userWithContext:[[BRCoreDataManager sharedManager] mainContext]];
[self.apiClient uploadProfilePicture:imageData forUser:user parameters:nil completion:[self uploadProfilePictureHandlerWithCompletion:completion]];
}
And then, also, here is the upload profile picture method in the API client:
- (void)uploadProfilePicture:(NSData *)imageData forUser:(BRUser *)user parameters:(NSDictionary *)parameters completion:(BRAPIClientCompletionBlock)completion {
NSError *error;
NSURLRequest *request = [self.requestSerializer multiformRequestForAPIAction:BRAPIActionCreate nestedResource:#"image" parent:user data:imageData parameters:parameters error:&error];
if (error) {
completion(nil, error);
}
NSURLSessionDataTaskCompletionBlock dataTaskCompletion = [self requestHandlerWithHTTPStatusErrors:#{ #400 : #(BRAPIUnauthorized) } completion:completion];
NSURLSessionDataTask *task = [self.authURLSession dataTaskWithRequest:request completionHandler:dataTaskCompletion];
[task resume];
}
and the multiform method called in the previous method:
- (NSURLRequest *)multiformRequestForAPIAction:(BRAPIAction)action nestedResource:(id)resource parent:(id)parent data:(NSData *)data parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing *)error {
NSParameterAssert(action);
NSParameterAssert(resource);
NSParameterAssert(parent);
NSParameterAssert(data);
NSString *method = [BRAPIRequestSerializer HTTPMethodForAPIAction:action];
NSURL *url = [self.apiURL nestedURLForResource:resource parent:parent];
NSLog(#"URL: %#",[url absoluteStringWithTrailingSlash]);
return [self.serializer multipartFormRequestWithMethod:method
URLString:[url absoluteStringWithTrailingSlash]
parameters:parameters
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data name:#"image" fileName:#"image.png" mimeType:#"image/png"];
}
error:error];
}
And my failed attempt at retrieving the data via the url it's stored at:
-(void) downloadImageFromURL :(NSString *)imageUrlString{
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:imageUrlString]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
NSData * receivedData = [[NSMutableData alloc] init];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!theConnection) {
// Release the receivedData object.
receivedData = nil;
// Inform the user that the connection failed.
NSLog(#"connection falied");
} else {
NSLog(#"connection succesful");
};
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"data: %#",data);
}
I also made sure to include the NSURLConnectionDelegate. The didRecieveData method is never called.
Feel free to let me know if there's more code you need to see!
About every single tutorial and example on the internet I see shows how to fetch JSON from some url and show it in Tableview. This is not my problem I know how to do that with AFNetworking framework or with native APIs.
My problem is that after I have downloaded the JSON, I want to show some of it in my UIView labels. I have actually succeeded doing this when I was trying to find a way around NSURLSession inability to cache in iOS 8. But I didn't realize that it was synchronous.
Factory.m
+ (Factory *)responseJson
{
static Factory *shared = nil;
shared = [[Factory alloc] init];
NSHTTPURLResponse *response = nil;
NSString *jsonUrlString = [NSString stringWithFormat:#"http://urltojson.com/file.json"];
NSURL *url = [NSURL URLWithString:[jsonUrlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSError *error = nil;
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:10.0];
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (error) {
NSLog(#"error");
} else {
//-- JSON Parsing
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:nil];
//NSLog(#"Result = %#",result);
shared.responseJson = result;
}
return shared;
}
My question is that is it possible to use for example AFNetwoking to do the same thing? Am I missing some method that I need to call like in case of a TableView
[self.tableView reloadData];
I would like to use that framework because I need to check Reachability and it seems to implement it already.
Edit as asked to show more code
ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self factoryLoad];
[self setupView];
}
- (void)factoryLoad
{
Factory *shared = [Factory responseJson];
self.titles = [shared.responseJson valueForKeyPath:#"data.title"];
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
});
}
- (void)setupView
{
self.issueTitleLabel.text = [self.titles objectAtIndex:0];
}
There are a couple oddities in the code you posted.
Factory, which appears to be a singleton class, should be instantiated inside a dispatch_once to ensure thread safety.
In ViewController.m, you are calling factoryLoad on the main thread, which is subsequently calling sendSynchronousRequest on the main thread. Apple's NSURLConnection Documentation warns against calling this function on the main thread as it blocks the thread, making your application unresponsive to user input.
You should not be passing in nil as the error parameter in NSJSONSerialization JSONObjectWithData:.
In your case I would recommend separating the fetching of data from the construction of your singleton object.
Factory.m
+(Factory *)sharedFactory {
static Factory *sharedFactory = nil;
dispatch_once_t onceToken;
dispatch_once(&onceToken, {
sharedFactory = [[Factory alloc] init];
});
}
-(void)fetchDataInBackgroundWithCompletionHandler:(void(^)(NSURLResponse *response,
NSData *data,
NSError *error)
completion {
NSHTTPURLResponse *response = nil;
NSString *jsonUrlString = [NSString stringWithFormat:#"http://urltojson.com/file.json"];
NSURL *url = [NSURL URLWithString:[jsonUrlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:10.0];
NSOperationQueue *downloadQueue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request
queue:downloadQueue
completionHandler:completion];
}
Now you should be able to create a reference to the data with a guarantee that the download request has finished and thus the data will exist.
ViewController.m
-(void)factoryLoad {
[[Factory sharedFactory] fetchDataInBackgroundWithCompletionHandler:^(void)(NSURLResponse *response, NSData *data, NSError *error){
if(!error) {
NSError *error2;
NSDictionary *serializedData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error2];
if(error2){ /* handle error */ }
self.titles = [serializedData valueForKeyPath:#"data.title"];
[Factory sharedFactory].responseJSON = serializedData;
}
else {
// handle error
}
}];
}
This will guarantee that the download has completed before you try to access any of the downloaded information. However, I've left a few things out here, including any sort of activity indicator displaying to the user that the app is doing something important in the background. The rest is, uh, left as an exercise to the reader.
Ok I took a deeper investigation into Morgan Chen's answer and how to block.
The example code took some modification but I think It works as it should and is better code.
In Factory.m
+ (Factory *) sharedInstance
{
static Factory *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
-(void)fetchDataInBackgroundWithCompletionHandler: (void(^)(BOOL success, NSDictionary *data, NSError *error)) block
{
NSString * baseURL = #"http://jsonurl.com/file.json";
AFHTTPRequestOperationManager * manager = [[AFHTTPRequestOperationManager alloc] init];
__weak AFHTTPRequestOperationManager *weakManager = manager;
NSOperationQueue *operationQueue = manager.operationQueue;
[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(#"internet!");
[weakManager.requestSerializer setCachePolicy:NSURLRequestReloadIgnoringCacheData];
[operationQueue setSuspended:NO];
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(#"no internet");
[weakManager.requestSerializer setCachePolicy:NSURLRequestReturnCacheDataElseLoad];
[operationQueue setSuspended:YES];
break;
default:
break;
}
}];
[manager.reachabilityManager startMonitoring];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[manager GET:baseURL parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (responseObject && [responseObject isKindOfClass:[NSDictionary class]]) {
block(YES, responseObject, nil);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) { // invalid request.
NSLog(#"%#", error.localizedDescription);
block(NO, nil, error);
}];
}
In ViewController.m I call this method on viewDidLoad
-(void)factoryLoad
{
[[Factory sharedInstance] fetchDataInBackgroundWithCompletionHandler:^(BOOL success, NSDictionary *data, NSError *error) {
if (success) {
NSLog(#"we have stuff");
self.responseData = data;
self.titles = [self.responseData valueForKeyPath:#"data.title"];
[self setupView];
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
});
}
}];
}
How do i convert id to an array?
I have an apple app that talks to a server.
Issue i have is the app returns the data in the form of id however i need to convert this to an array as the actual returned data looks like the following.
[["30","2",1],["15","67",1],["30","4",1]]
It is actually the output from a mysql server
The actual app code looks like the following
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"blah blah"]];
NSURL_Layer * connection = [[NSURL_Layer alloc]initWithRequest:request];
[connection setCompletitionBlock:^(id obj, NSError *err) {
if (!err) {
//Need to convert the id to nsarray here, dunno how
} else {
//There was an error
}
}];
[connection start];
The NSURL.h
-(id)initWithRequest:(NSURLRequest *)req;
#property (nonatomic,copy)NSURLConnection * internalConnection;
#property (nonatomic,copy)NSURLRequest *request;
#property (nonatomic,copy)void (^completitionBlock) (id obj, NSError * err);
-(void)start;
NSURL.m
-(id)initWithRequest:(NSURLRequest *)req {
self = [super init];
if (self) {
[self setRequest:req];
}
return self;
}
-(void)start {
container = [[NSMutableData alloc]init];
internalConnection = [[NSURLConnection alloc]initWithRequest:[self request] delegate:self startImmediately:YES];
if(!sharedConnectionList)
sharedConnectionList = [[NSMutableArray alloc] init];
[sharedConnectionList addObject:self];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[container appendData:data];
}
//If finish, return the data and the error nil
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
if([self completitionBlock])
[self completitionBlock](container,nil);
[sharedConnectionList removeObject:self];
}
//If fail, return nil and an error
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
if([self completitionBlock])
[self completitionBlock](nil,error);
[sharedConnectionList removeObject:self];
}
Update:
i have added
NSURL_Layer * connection = [[NSURL_Layer alloc]initWithRequest:request];
[connection setCompletitionBlock:^(id obj, NSError *err) {
if (!err) {
NSError* error;
NSArray* array = [NSJSONSerialization JSONObjectWithData:obj options:NSJSONReadingAllowFragments error:&error];
} else {
//There was an error
}
}];
[connection start];
but returns error
error NSError * domain: #"NSCocoaErrorDomain" - code: 3840
_userInfo NSDictionary * 1 key/value pair
[0] (null) #"NSDebugDescription" : #"Invalid value around character 0."
Update: I put
NSLog(#"Data as string:%#", [[NSString alloc]initWithData:obj encoding:NSUTF8StringEncoding]);
which gave me a strange feedback. As a result i looked at my url request full code is below.
NSString *post = [NSString stringWithFormat:#"unique_id=%#&unique_password=%#",ServerUniqueID,ServerPassword];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[post length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"blah blah"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSURL_Layer * connection = [[NSURL_Layer alloc]initWithRequest:request];
[connection setCompletitionBlock:^(id obj, NSError *err) {
if (!err)
{
NSError* error;
NSArray* array = [NSJSONSerialization JSONObjectWithData:[NSData dataWithData: obj] options:NSJSONReadingAllowFragments error:&error];
NSLog(#"Data as string:%#", [[NSString alloc]initWithData:obj encoding:NSUTF8StringEncoding]);
int temp = array.count;
}
else
{
//There was an error
}
}];
[connection start];
if i remove
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
it works
if its in it dosn't so i have a whole new issue to look into.
you save the bytes into a container variable, alas the id infact NSData
(note id is just a 'wildcard pointer' that means ANY objC object)
so your id is NSData and from what you show it seems to be 3 json arrays... but no real JSON... (["30","2",1]["15","67",1]["30","4",1] isn't anything)
EITHER make the server send you JSON and THAT you can parse into a dictionary/array using NSJSONSerialization
OR write a custom separator to convert the data
I have two methods and I need to use a variable from first as input parameter in the second. How can I do it? My code is :
First method
-(NSString*)getResponseData :(NSString*) apiHttp {
NSString *code = #"&code=";
NSString *finalLink = [[NSString alloc] initWithFormat:#"%#%#",apiHttp,phoneNumber];
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:finalLink]];
NSLog(#"%#", finalLink);
__block NSDictionary *json;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
json = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
NSLog(#"Async JSON: %#", json);
NSError *error;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
myString = [jsonDict objectForKey:#"result"];
// NSLog(#"%#", myString);
}];
return myString;
}
Second method:
-(void)showCodeView:(NSString*) ifString{
if([ifString isEqualToString:#"200"]){
aPasswordField.hidden = NO;
[aPasswordField setBorderStyle:UITextBorderStyleLine];
aPasswordField.layer.cornerRadius=1.0f;
aPasswordField.layer.masksToBounds=YES;
aPasswordField.layer.borderColor=[[UIColor whiteColor]CGColor];
aPasswordField.layer.borderWidth= 0.8f;
UIColor *color = [UIColor lightTextColor];
aPasswordField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:#"Код" attributes:#{NSForegroundColorAttributeName: color}];
self.aPasswordField.delegate = self;
}
}
And this is how I call them:
[self getResponseData:apiHttp];
[self showCodeView:myString];
So I can't understand why my myString is null after [self getResponseData:apiHttp]; was called even if my method retutns it.
You are calling two methods after another but are missing that the first one is asynchronous.
When you call sendAsynchronousRequest:queue:completionHandler: it will perform the request asynchronously (not waiting) and call the completion block once it has a response. Since the code doesn't wait for this to happen, getResponseData: immediately returns the current value of myString which is nil if it's not set yet.
You can see how this is working by adding a some log statements before and after each method call:
NSLog(#"Before getResponseData:");
[self getResponseData:apiHttp];
NSLog(#"After getResponseData:");
NSLog(#"Before showCodeView:");
[self showCodeView:myString];
NSLog(#"After showCodeView:");
and the same for the asynchronous request
NSLog(#"Before sendAsynchronousRequest:");
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *connectionError) {
NSLog(#"After sendAsynchronousRequest:");
// the rest of the completion block ...
There are many ways to deal with this. One would be to add a block argument for the getResponseData: method that is called from the completion handler of the request.
If you are unused to working with blocks, a simpler but more tightly coupled alternative is to call [self showCodeView:myString]; from inside of the completion handler.
You want to perform showCodeView only when your asynchronous getResponseData finishes, so implement your own rendition of the completion block pattern:
- (void)getResponseData :(NSString*) apiHttp completionHandler:(void (^)(NSDictionary *, NSError *))completion {
NSString *code = #"&code=";
NSString *finalLink = [[NSString alloc] initWithFormat:#"%#%#",apiHttp,phoneNumber];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:finalLink]];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (completion) {
if (connectionError) {
completion(nil, connectionError);
} else {
NSError *parseError;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
completion(json, parseError);
}
}
}];
}
Note, I've eliminated that __block variable and changed the return type to void (since this doesn't return anything ... the value is passed back via the completion block).
You can then do:
[self getResponseData:apiHttp completionHandler:^(NSDictionary *json, NSError *error) {
if (error) {
// handle this however appropriate for your app
} else {
NSString *myString = json[#"result"];
[self showCodeView:myString];
}
}];