Wait to return until object has value - ios

I have a method that gets called that looks like this:
- (NSString *)getClassNamesWithClassID: (NSNumber *) classID {
NSLog(#"Gettting Name for classID: %#", classID);
NSURLRequest *classIDRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://dphs.edu20.org/api/get_classes_with_ids?api_key=%#&classIDs=%#", apiKey, classID]]];
NSURLConnection *classIDConnection = [[NSURLConnection alloc] initWithRequest:classIDRequest delegate:self];
return className;
}
Below this, there are all of the delegate methods for the NSURLConnection. The variable className is set in the delegate's 1connectionDidFinishLoading1 method. However, it needs to be returned in the above getClassNamesWithClassID method. When I try to call the getClassNamesWithClassID method, the method always returns nil, presumably because the NSURLConnection takes some time to receive the data and then set it to the className variable but the return is happening before all of this occurs. How can I have the method "wait" until the NSURLConnection is complete until it returns className?
EDIT
Synchronous connection code:
- (NSString *)getClassNamesWithClassID: (NSNumber *) classID {
classDataArray = [[NSMutableArray alloc] init];
NEOAPIData = [[NSMutableData alloc] init];
NSLog(#"Gettting Name for classID: %#", classID);
NSURLRequest *classIDRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://dphs.edu20.org/api/get_classes_with_ids?api_key=%#&classIDs=%#", apiKey, classID]]];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:classIDRequest
returningResponse:&response
error:&error];
if (data)
{
[NEOAPIData appendData:data];
NSString *strData = [[NSString alloc]initWithData:NEOAPIData encoding:NSUTF8StringEncoding];
NSLog(#"%#",strData);
classDataArray = [NSJSONSerialization JSONObjectWithData:NEOAPIData options:NSJSONReadingMutableLeaves error:nil];
return #"Hello";
}
return #"Error";
}

Short answer: You can't, and you shouldn't.
NSURLConnection (which is now deprecated BTW) is an async method. It returns immediately, before the network request has even been sent, much less data received.
What you need to do is refactor your getClassNamesWithClassID method to be a method that takes a completion block with the class name as a parameter to that block. Then write your code so that it saves the completion block to an instance variable and invokes it in the connectionDidFinishLoading delegate method. Then write the caller to pass in code that should be invoked once the class name is known.

You need call NSURLConnection synchronous like following code
- (NSString *)getClassNamesWithClassID: (NSNumber *) classID {
NSLog(#"Gettting Name for classID: %#", classID);
NSURLRequest *classIDRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://dphs.edu20.org/api/get_classes_with_ids?api_key=%#&classIDs=%#", apiKey, classID]]];
// NSURLConnection *classIDConnection = [[NSURLConnection alloc] initWithRequest:classIDRequest delegate:self];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:classIDRequest
returningResponse:&response
error:&error];
if (error == nil)
{
// Parse data here
return className;
}
return nil;
}

Related

Use NSArray object outside from json object

I'm new to Objective-C, just wondering how to use NSArray object outside from JSON.
For example:
NSURL *url = [NSURL URLWithString:#"http://acumen-locdef.elasticbeanstalk.com/service/countries"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
NSMutableArray *greeting = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
for (NSDictionary *countryList in greeting) {
[myFinalListArray addObject:countryList[#"name"]];
}
}
NSLog(#"%#",myFinalListArray); //(This one showing all results..)
}];
NSLog(#"%#",myFinalListArray); //(This one giving empty result)
I have defined myFinalListArray and added objects in for loop.
If you use NSLog inside the loop or outside the loop it will show you results. But if I use this after }]; (after the code is ending.),
it's giving me empty array.
If you are accessing myFinalListArray in tableview then you can reload tableview inside the block after fetching data.
Or if you are accessing this array in some other task then you have to make notification call (have to add observer) and then post notification that will call some other method and access your array there and do your further stuff.
The block of code associated with sendAsynchronousRequest isn't executed until the network fetch has completed; this takes some time. While the network fetch is happening your code continues to execute, starting with the line immediately after sendAsynchronousRequest which is NSLog(#"%#",myFinalListArray); - but because the network operation hasn't completed you get an empty array.
In the block you need to include the code that you need to process the array, update your user interface or whatever (If you update UI make sure you dispatch the operation on the main thread)
This will work. You can try with this.
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
//Pass here the reference the a array. It will return you the array of you county when downloaded complete.
[self getURLResponse:&myFinalListArray];
NSLog(#"countryArray:%#",myFinalListArray);
}
-(void)getURLResponse:(NSMutableArray**)countryArray{
NSURL *url = [NSURL URLWithString:#"http://acumen-locdef.elasticbeanstalk.com/service/countries"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
NSURLResponse *response;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:
request returningResponse:&response error:&error];
NSMutableArray *greeting = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
for (NSDictionary *countryList in greeting) {
[myFinalListArray addObject:countryList[#"name"]];
}
*countryArray = [[NSMutableArray alloc]initWithArray:myFinalListArray copyItems:YES];
}
-(void)sendRequest
{
NSURL *url = [NSURL URLWithString:#"http://acumen-locdef.elasticbeanstalk.com/service/countries"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError) {
if (data.length > 0 && connectionError == nil)
{
NSMutableArray *greeting = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
if( !myFinalListArray )
{
myFinalListArray=[[NSMutableArray alloc]init];
}
for (NSDictionary *countryList in greeting) {
[myFinalListArray addObject:countryList[#"name"]];
}
}
[self printArray];
}];
}
//create method that will execute after response
-(void) printArray
{
NSLog(#"%#",myFinalListArray); //(This one showing all results..)
}
Use
__block NSMutableArray *myFinalListArray = [[NSMutableArray alloc] init];
This should work.
Happy Coding.
sendAsynchronousRequest runs asynchronously, meaning that the code below is already performed while the request is still running, so the NSLog is logging the empty array.
Only when the request finishes, the array is filled up but your outer NSLog was already performed.

how to access json data assigned to property in user defined method

I am working on the google maps where I find the location based on the text entered in the
textfield.I have used NSConnection to find the location json and then I assign this json to
the property in connectionDidFinishLoading delegate method so that I can access the json
when it is required but unfortunately I am not getting the data in geocodeAddress
method(NSLog(#"geodata %#",geocode))
Can any one help me to fix this issue?
- (void)geocodeAddress:(NSString *)address withCallback:(SEL)callback withDelegate: (id)delegate
{
NSString *geocodingBaseUrl = #"http://maps.googleapis.com/maps/api/geocode/json?";
NSString *url = [NSString stringWithFormat:#"%#address=%#&sensor=false", geocodingBaseUrl,address];
NSLog(#"url=%#",url);
url = [url stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
NSURL *queryUrl = [NSURL URLWithString:url];
NSURLRequest *request =[NSURLRequest requestWithURL:queryUrl cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:10];
//NSURLRequest *request =[NSURLRequest requestWithURL:url];
downloaddata= [[NSMutableData alloc]init];
NSURLConnection *connection =[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"geodata %#",geocode);
//[delegate performSelector:callback];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSError *error;
alldata = [NSJSONSerialization
JSONObjectWithData:self->downloaddata
options:NSJSONReadingMutableContainers
error:&error];
if(error)
{
}
else
{
self.geocode =alldata;
//NSLog(#"geodata %#",geocode);
}
}
NSURLConnection's are normally asynchronous, so geocode isn't set until the connectionDidFinishLoading is called, which won't happen until sometime after geocodeAddress... has finished. In your connectionDidFinishLoading... you'll need to execute code to cause the UI to refresh with the now valid geocode data.
Alternatively you could use + (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error to execute the request synchronously, but that would be bad since it would block the UI until the request completed.

HTTP Request through JavaScriptCore in iOS7

I am trying to use one of iOS7 new features, the JavaScriptCore Framework. I can successfully output a helloWorld string from Javascript, but what I'm interested in, is doing HTTP POSTs in Javascript and then pass the response to Objective-C. Unfortunately, when I'm creating an XMLHttpRequest object in Javascript, I get EXC_BAD_ACCESS (code=1, address=....).
Here is the Javascript code (hello.js):
var sendSamplePost = function () {
// when the following line is commented, everything works,
// if not, I get EXC_BAD_ACCESS (code=1, address=....)
var xmlHttp = new XMLHttpRequest();
};
var sayHello = function (name) {
return "Hello " + name + " from Javascript";
};
Here is the Objective-C code inside my ViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
JSContext *context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]];
NSString *scriptPath = [[NSBundle mainBundle] pathForResource:#"hello" ofType:#"js"];
NSLog(#"scriptPath: %#", scriptPath);
NSString *script = [NSString stringWithContentsOfFile:scriptPath encoding:NSUTF8StringEncoding error:nil];
NSLog(#"script: %#", script);
[context evaluateScript:script];
JSValue *sayHelloFunction = context[#"sayHello"];
JSValue *returnedValue = [sayHelloFunction callWithArguments:#[#"iOS"]];
// this works!
self.label.text = [returnedValue toString];
JSValue *sendSamplePostFunction = context[#"sendSamplePost"];
// this doesn't work :(
[sendSamplePostFunction callWithArguments:#[]];
}
Could it be that HTTP Requests functionality is not provided in JavaScriptCore Framework? If yes, could I overcome this by using UIWebView's -stringByEvaluatingJavaScriptFromString:? What if I compiled and included in my project another Javascript Engine (e.g. V8)?
XMLHttpRequest is, as stated before, not part of JavaScript, but you still can wrap the iOS URLRequest so it's available in your JS.
in JSUtils.h
#protocol UtilsExport;
#interface JSUtils : NSObject <UtilsExport>
#end
#protocol UtilsExport <JSExport>
- (void)get:(NSString *)url then:(JSValue *)jsCallback;
#end
in JSUtils.m
#import "JSUtils.h"
- (void)get:(NSString *)url then:(JSValue *)callback {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
[request setHTTPMethod:#"GET"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ([data length] > 0 && error == nil) {
[callback callWithArguments:#[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding], #YES]];
}
}];
}
Next, bind the instance to the JSContext somewhere in your code
JSContext *context = [[JSContext alloc] init];
context[#"utils"] = [[JSUtils alloc] init];
from your JS file, you can now call
utils.getThen('http://localhost/api/dashboard', function(resultString){
console.log(resultString)
}
You could also use a block and bind it straight to the JSContext to get the same result.
My guess would be that HTTP Requests are not part of JavaScript Core, as it's really part of the browser, not the JavaScript Language.
I would assume that JavaScript core only includes what's in the ECMAScript definition.
If you want AJAX, then the WebView is the way to go.
JSContext *context = [[JSContext alloc] init];
context[#"request"] = ^(NSString *url) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:10];
NSURLResponse *response = nil;
[request setHTTPMethod:#"GET"];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSString *body = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return body;
};
In JS:
var body = request(url);

How can I send multiple url request from a NSURLConnection delegate?

This is the logical flow for my application:
At first, when the view controller has finished loading, then a NSURLConnection request can start its execution
The response consists in xml data
After parsing that xml I need to send another NSURLConnection request.
After sending the second request, if the response is ok, I receive other xml data
After parsing the second xml, I have to check some issues between first and second xml data.
So, is it possible to send multiple request? How? I do not need code, you could just explain it.
Thank you in advance.
I do this with the NSURLConnection Making them properties, then checking which one it is:
#property (nonatomic,retain) NSURLConnection *myConnection;
#property (nonatomic,retain) NSURLConnection *mySecondConnection;
then in the delegate:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if (connection == myConnection){
//do something
}
if (connection == mySecondConnection){
// do something else
}
}
You can pass your NSURLRequest to the connection:
self.myConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
There is a third party library available which is a wrapper on CFNetwork is ASIHTTPREQUEST
This Library should do work for you. so that you don't have to write the code from scratch. other alternative is create one class which will be responsible for creating NSURLConnection then sending the request and finally notify to view controller using delegate or notification one data is received .
- (void)viedDidLoad{
[super viewDidLoad];
[self firstRequestMethod];
}
- (void)firstRequestMethod{
NSString *myFirstRequestURL = #"<URL>";
NSURL *webURL = [NSURL URLWithString:myFirstRequestURL];
NSURLRequest *request = [NSURLRequest requestWithURL:webURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSError *error;
NSURLResponse *response;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(returnData)
{
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSASCIIStringEncoding];
//Parse your response here.
//Is desired response obtain call the second Request, as described above
if (TRUE) { //on success
[self secondRequestMethod];
}
}
}
- (void)secondRequestMethod{
NSString *mySecondRequestURL = #"<URL>";
NSURL *webURL = [NSURL URLWithString:mySecondRequestURL];
NSURLRequest *request = [NSURLRequest requestWithURL:webURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSError *error;
NSURLResponse *response;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(returnData)
{
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSASCIIStringEncoding];
//Parse your response here.
//Is desired response obtain call the second Request, as described above
if (TRUE) { //on success
//subsequent calls to other url, same as above
}
}
}
Hope this will help you understand better....

Errors when using two Asynchronous ASIHTTP Requests in different classes

Suppose i have two classes A & B. class A calls the asynchronus method Foo in class B. Foo method fetches data using asynchronous ASIHTTPRequest and send the data from Foo back as the return value to Class A. Class A will use that returned data and do the things
I am creating a object of my class URLParser here in another class and calling the function getJsonUrl , it will parse and get the json url for me . I am using that returned URL in another ASIHTTPRequest here . But i am getting EXC_BAD_ACCESS ...help me to figure it out ....
......
URLParser *urlParser = [[URLParser alloc] init];
NSString *JsonString = [urlParser getJsonUrl:#"http://mywebs.com/?q=iphone/news"];
NSLog(#" url returned = %#" ,JsonString);
NSURL *JsonUrl = [NSURL URLWithString:JsonString];
newsRequest = [ASIHTTPRequest requestWithURL:JsonUrl];
[newsRequest setDelegate:self];
[newsRequest startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
newsDictionary = [[NSMutableDictionary alloc] init];
NSData *responseData = [request responseData];
NSString *response = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding] ;
self.newsDictionary = [response JSONValue];
[response release];
[self getDataNews:self.newsDictionary];
}
URL Parser Class
#synthesize albumDic;
#synthesize GlobalRequest;
-(NSString*)getJsonUrl:(NSString *)url{
GlobalRequest = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:url]];
[GlobalRequest setDelegate:self];
[GlobalRequest startAsynchronous]; // when i called the [GlobalRequest startSynchronous] ....both cases m getting the same error
return JsonStr;
}
- (void)requestFinished:(ASIHTTPRequest *)request{
albumDic = [[NSMutableDictionary alloc] init];
NSData *responseData = [request responseData];
NSString *response = [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding] ;
self.albumDic = [response JSONValue];
[response release];
[self GetDictionary:self.albumDic];
}
- (void)requestFailed:(ASIHTTPRequest *)request{
[request cancel];
}

Resources