Get data from asynchronous requests in right order - iphone

I need to load data from my API without waiting 20s each time I launch my application.
So I use:
NSURL *myUrlCourses = [[NSURL alloc] initWithString:url];
NSMutableURLRequest *request = [NSURLRequest requestWithURL: myUrlCourses];
NSURLConnection *connexion = [[NSURLConnection alloc] initWithRequest:request delegate:self];
for my 10 first request in while loop which permit to get data in background.
But, when I get data from this request with:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{}
The result don't match.
So, I think I have to use thread or something like that to get the right data for each request but I don't really know how!?
Could you help me to solve this problem?
Thanks

Store all of your connections in variables with describing names and then compare the pointer values.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if(connection == _theFacebookConnection)
{
//Handle Facebook code
}
else if(connection == _theTwitterConnection)
{
//Handle Twitter code
}
}

I use a CFMutableDictionaryRef to save another mutable dictionary for each connection. This inner dictionary can hold as much data as you want.
like this:
#interface Foo {
CFMutableDictionaryRef connections;
}
#implementation Foo
- (id)init {
self = [super init];
if (self) {
connections = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
return self;
}
- (BOOL)addURLRequest:(NSURLRequest *)request successSelector:(SEL)successSelector errorSelector:(SEL)errorSelector {
NSMutableDictionary *connectionInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSMutableData data], #"receivedData",
[NSValue valueWithPointer:successSelector], #"successSelector",
[NSValue valueWithPointer:errorSelector], #"errorSelector",
request, #"request",
nil];
NSURLConnection *connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO] autorelease];
CFDictionaryAddValue(connections, connection, connectionInfo);
[connection start];
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSMutableDictionary *connectionInfo = (NSMutableDictionary *)CFDictionaryGetValue(connections, connection);
[[connectionInfo objectForKey:#"receivedData"] appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSMutableDictionary *connectionInfo = (NSMutableDictionary *)CFDictionaryGetValue(connections, connection);
NSData *data = [connectionInfo objectForKey:#"receivedData"];
LogInfo(#"Finished Connection %#", connection);
SEL selector = [[connectionInfo objectForKey:#"successSelector"] pointerValue];
if ([self respondsToSelector:selector]) {
[self performSelector:selector withObject:data];
}
CFDictionaryRemoveValue(connections, connection);
}

I use ASIHTTPRequest for this sort of thing because I can use [request setUserInfo:(NSDictionary *)] to specify additional data that travels around with the request responses.
Then when I receive each response, I can look at that requests UserInfo dictionary and process the data accordingly.
What's nice about this is you can put as much or as little data into the UserInfo Dictionary as you require.

Related

Asynchronous NSURLConnection getting failed with auto mistypecasting of class instance

I am creating a single instance of a chase class which has a dictionary as a property.
As I add elements to dictionary, I can see the change in the count of dictionary keys.
But when I am accessing, it's firing a crash with message like given below:
[__NSCFString connection:didReceiveData:];
[__NSCFArray reqmap]; etc..
Basically, I am mapping request url with NSData instance, so that the response could be mapped and appended properly, asynchronously.
Code:
-(id)init
{
self = [super init];
if (self)
{
self.cacheDict = [[NSMutableDictionary alloc] initWithCapacity:20];
self.reqmap = [[NSMutableDictionary alloc] initWithCapacity:20];
}
return self;
}
+ (CommunityImageCache*)getSharedCommunityImageCache
{
if (sharedCommunityImageCacheInstance == nil) {
sharedCommunityImageCacheInstance = [[super allocWithZone:NULL] init];
}
return sharedCommunityImageCacheInstance;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSString* keyurl = [[[connection currentRequest] URL] absoluteString];
//crash point >>>>
NSMutableData* tempdata = (NSMutableData*)[self.reqmap objectForKey:keyurl];
[tempdata appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection*)theConnection
{
NSString* keyurl = [[[theConnection currentRequest] URL] absoluteString];
NSMutableData* tempdata = [self.reqmap objectForKey:keyurl];
UIImage* img = [UIImage imageWithData:tempdata];
[self.cacheDict setObject:img forKey:[#"image:\\\\public\\" stringByAppendingString:keyurl]];
[self.reqmap removeObjectForKey:keyurl];
}
-(UIImage *)getImageFromUrl:(NSString *)url
{
UIImage* image = nil;
image = [self.cacheDict objectForKey:url];
if (!image)
{
image = [UIImage imageNamed:#"defaultProfile.png"];
//dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSString* localUrl = [NSString stringWithString:url];
NSString* finurl = [localUrl substringFromIndex:[#"image:\\\\public\\" length]];
UIImage* img = nil;
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:finurl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSMutableData* data = [[NSMutableData alloc] init];
NSURLConnection* connect = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
NSString* keyurl = [[[connect currentRequest] URL] absoluteString];
[self.reqmap setObject:data forKey:keyurl];
}
}
Needed badly, Thanks in advance.
Well, reqmap is suppose to contain NSMutableData objects, right ?
They need instantiation, something like :
[self.reqmap setValue:[[NSMutableData alloc] init] forKey:#"TheKey"];
You should also implement
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
when you're using NSURLConnection. I don't see it in your code above. Then you should alloc/reset the data in this function.
What is the actual error you are seeing? It sounds like either reqmap isn't an NSDictionary (though it is in your code above), or the object you're retrieving isn't a NSMutableData.
In total, implement:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void)connectionDidFinishLoading:(NSURLConnection *)connection

iOS 5 NSURLConnection with NSOperationQueue - Providing UI Feedback

I need to make multiple NSURLConnections to a JSON Web Service. I would like each WS call to keep in UI informed, probably with a UIActivityIndicatorView and label. So far I've created a NSURLConnection helper class to handle the connection and placed the URL delegates in the View. This works great for updating the UI with a single WS call.
For multiple calls, I'm trying to use an NSOperationQueue. I'd like to setMaxConcurrentOperationCount to one on the queue so that each Operation executes one at a time. Here's the relevant code on my View Controller:
ViewController.m
#import "URLOperationHelper.h"
#implementation ViewController
- (IBAction)showPopup:(id)sender
{
// Dictonary holds POST values
NSMutableDictionary *reqDic = [NSMutableDictionary dictionary];
// Populate POST key/value pairs
[reqDic setObject:#"pw" forKey:#"Password"];
[reqDic setObject:#"ur" forKey:#"UserName"];
operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:1];
[operationQueue cancelAllOperations];
[operationQueue setSuspended:YES];
URLOperationHelper *wsCall1 = [[URLOperationHelper alloc] initWithURL:#"urlString1" postParameters:reqDic urlDelegate:self];
URLOperationHelper *wsCall2 = [[URLOperationHelper alloc] initWithURL:#"urlString2" postParameters:reqDic urlDelegate:self];
[operationQueue addOperation:wsCall1];
[operationQueue addOperation:wsCall2];
}
// Did the URL Connection receive a response
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"Did receive response: %#", response);
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
int code = [httpResponse statusCode];
// Handle status code here
webData = [[NSMutableData alloc]init];
}
// Did the URL Connection receive data
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Did receive data: %#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
assert(webData != nil);
[webData appendData:data];
}
// Did the connection fail with an error?
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"%#", error);
}
// Executes after a successful connection and data download
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Connection finished");
}
#end
And here is my URLOperationHelper.m
#implementation URLHelper
- (id)initWithURL:(NSString *)urlPath
postParameters:(NSMutableDictionary *)postParameters
urlParentDelegate:(id) pDelegate
{
if(self = [super init])
{
connectionURL = urlPath;
postParams = postParameters;
parentDelegate = pDelegate;
}
return self;
}
- (void)done
{
// Cancel the connection if present
if(urlConnection)
{
[urlConnection cancel];
urlConnection = nil;
}
// Alert
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
executing = NO;
finished = YES;
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
}
- (void)cancel
{
// Possibly add an NSError Property
[self done];
}
- (void)start
{
// Make sure this operation starts on the main thread
if(![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:NO];
return;
}
// Make sure that the operation executes
if(finished || [self isCancelled])
{
[self done];
return;
}
[self willChangeValueForKey:#"isExecuting"];
executing = YES;
[self main];
[self willChangeValueForKey:#"isExecuting"];
}
- (void)main
{
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:postParams options:NSJSONWritingPrettyPrinted error:&error];
// Convert dictionary to JSON
NSString *requestJSON = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(#"JSONRequest: %#", requestJSON);
// Declare Webservice URL, request, and return data
url = [[NSURL alloc] initWithString:connectionURL];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSData *requestData = [NSData dataWithBytes:[requestJSON UTF8String] length:[requestJSON length]];
// Build the request
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:requestData];
// Connect to Webservice
// Responses are handled in the delegates below
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:parentDelegate startImmediately:YES];
}
- (BOOL)isConcurrent
{
return YES;
}
- (BOOL)isExecuting
{
return executing;
}
-(BOOL)isFinished
{
return finished;
}
#end
The problem that I'm having is the Start method for the URLOperation is never called. The OperationQueue is created and the Operations are called, but nothing happens after that, execution or thread wise.
Also, is this a correct line of thinking to provide UI feedback using NSOperationQueues like this? I.E. calling the NSURLDelegates from the Operation?
If you set setSuspended to YES before adding the Operations then your Operations will be queued into a suspended queue.. i suggest not to suspend the queue at
furthermore, your operation never ends anyway. You need to assign the operation itself as the delegate and implement all necessary delegate methods. In these methods you can forward the messages to your parentDelegate and decide when you are finished and call your done method when appropriate (i suggest connection:didFailWithError: and connectionDidFinishLoading:)
There is a good tutorial here: http://blog.9mmedia.com/?p=549
You are also not completely implementing key-value-coding compilant properties correct. Whenever you call willChangeValueForKey: you also need to call didChangeValueForKey afterwards:
- (void)start
{
...
[self willChangeValueForKey:#"isExecuting"];
executing = YES;
[self didChangeValueForKey:#"isExecuting"];
[self main];
}
and:
- (void)done
{
...
// Alert
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
executing = NO;
finished = YES;
[self didChangeValueForKey:#"isFinished"];
[self didChangeValueForKey:#"isExecuting"];
}
See this Q/A for KVC: when to use "willChangeValueForKey" and "didChangeValueForKey"?

Second asynchronous NSURLConnection doesn't call delegate methods

From my ApplicationDelegate, I'm doing an NSURLConnection fetch over the network (it's wrapped in a class, as you'll see below). This one seems to work correctly: I get all the data in didReceiveData and I get the completion call connectionDidFinishLoading. At the end of connectionDidFinishLoading, I instantiate one or more of a slightly different kind of wrapper class, but they're essentially the same thing. The problem is that the second NSURLConnection's delegate is never having it's methods called.
I've looked at many different answers, but all to no avail. I'm not spawning any new threads and all the [NSThread isMainThread] checks I've littered throughout the code return true.
I'm stumped. Can anyone help me out? Here's the relevant code:
App Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
ConnectionWrapper* w = [[ConnectionWrapper alloc] initWithParams:self
url:[NSURL URLWithString:<url>]];
[w beginFetch];
return YES;
}
...
-(void)fetchCompleted:(NSURL*)url directory:(NSString*)directory
{
NSLog(#"fetch completed");
}
-(void)fetchFailed:(NSURL*)url
{
NSLog(#"fetch failed");
}
...
ConnectionWrapper:
-(id)initWithParams:(id<ConnectionWrapperDelegate>)d url:(NSURL*)url
{
delegate = d;
connURL = url;
return [self init];
}
-(void)beginFetch
{
NSURLRequest* request = [[NSURLRequest alloc] initWithURL:connURL];
NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[conn release];
[request release];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"append");
[responseData appendData:data];
}
- (void) connectionDidFinishLoading: (NSURLConnection*) connection
{
... parsing ....
DifferentConnectionWrapper* w = [[DifferentConnectionWrapper alloc] initWithParams:self
url:[NSURL URLWithString:<different url>]];
[w beginFetch];
}
-(void)fetchCompleted:(NSURL*)URL
{
NSLog(#"completed: %#", URL);
}
-(void)fetchFailed:(NSURL*)URL
{
NSLog(#"failed");
}
DifferentConnectionWrapper:
-(id)initWithParams:(id)d url:(NSURL*)url
{
delegate = d;
connURL = url;
return [self init];
}
-(void)beginFetch
{
NSURLRequest* request = [[NSURLRequest alloc] initWithURL:connURL];
NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[conn release];
[request release];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"append");
[responseData appendData:data];
}
- (void) connectionDidFinishLoading: (NSURLConnection*) connection
{
... parsing ....
DifferentConnectionWrapper* w = [[DifferentConnectionWrapper alloc] initWithParams:self
url:[NSURL URLWithString:<different url>]];
[w beginFetch];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"got response");
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"got data");
[responseData appendData:data];
}
- (void) connectionDidFinishLoading: (NSURLConnection*) connection
{
NSLog(#"image saver completed: %#", connURL);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"error");
}
ConnectionWrapper and DifferentConnectionWrapper have similar functions, but there's other logic that I've omitted here for brevity.
thanks for the help. I appreciate it.
A couple of things: I don't see a didFailWithError: in your first wrapper class, and (a little off topic) are you leaking memory with your DifferentConnectionWrapper *w ?
Anyway, what I would try is: see if you can invoke DifferentConnectionWrapper directly from the appDelegate instead of the ConnectionWrapper.
And I would try to decouple the two calls in any event. When the first one finishes, and calls appDelegate, can't you launch your DifferentConnectionWrapper from there?
I realize this doesn't explain your problem, but you might get it working (and which of THOSE two things is more important, is an entirely different debate.)
I believe the problem is that you’re releasing the URL connection in -beginFetch:
-(void)beginFetch
{
NSURLRequest* request = [[NSURLRequest alloc] initWithURL:connURL];
NSURLConnection* conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[conn release];
[request release];
}
The URL connection object should be kept alive and released when the connection has finished loading:
- (void) connectionDidFinishLoading: (NSURLConnection*) connection
{
... parsing ....
// *** Release the connection and whatever data you’ve kept related to
// this particular connection
[connection release];
[responseData release];
// *** or [responseData setLenght:0]; depending on how you’re
// managing responseData
DifferentConnectionWrapper* w = [[DifferentConnectionWrapper alloc] initWithParams:self
url:[NSURL URLWithString:<different url>]];
[w beginFetch];
}
or when there’s been an error:
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// *** Release the connection and whatever data you’ve kept related to
// this particular connection
[connection release];
[responseData release];
// *** or [responseData setLenght:0]; depending on how you’re
// managing responseData
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
Edit: Your initialiser is a tad weird:
-(id)initWithParams:(id<ConnectionWrapperDelegate>)d url:(NSURL*)url
{
delegate = d;
connURL = url;
return [self init];
}
There’s no way to know what happens unless we see the code for -init and, at any rate, this should be the designated initialiser, so it shouldn’t be sending -init to self anyway. Furthermore, you should be retaining the url object that’s being passed to the initialiser.
The following makes more sense:
-(id)initWithParams:(id<ConnectionWrapperDelegate>)d url:(NSURL*)url
{
self = [super init];
if (self) {
delegate = d;
connURL = [url retain];
}
return self;
}
Don’t forget to release the url object in -dealloc or when you’re assigning another value to connURL.
OK. It turns out that this bug was caused by something I missed. I was going into a hard spin right after that second request, which would probably screw up just about anything. Once I fixed that problem, everything worked just fine.
Thanks for the help.

NSURLConnection and grand central dispatch

Is it advisable to wrap up NSUrlConnection in a gcd style blocks and run it on a low_priority queue?
I need to ensure that my connections are not happening on the main thread and the connections need to be asynchronous. I also need several simultaneous requests to go at once.
If I go the gcd route, I'm not sure which thread the NSUrlConnectionDelegate methods get invoked on.
NSURLConnection relies on delegates so once the connection is complete, whatever wrapper class that handles it will need to invoke its caller. I'm not certain how to deal with all of the various callbacks that are invoked when the connection work starts up/finishes:
- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)incrementalData;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
Should I just call the synchronous versions but wrapped in gcd blocks? And if I want to cancel a call, use 'dispatch_suspend'?
dispatch_async(queue,^{
NSString* result = [self mySynchronousHttp:someURLToInvoke];
});
// If I need to cancel
dispatch_suspend(queue);
I recommend you to see WWDC Sessions about network application in iPhone OS.
WWDC 2010 Session 207 - Network Apps for iPhone OS, Part 1
WWDC 2010 Session 208 - Network Apps for iPhone OS, Part 2
The lecturer said
"Threads Are Evil™"
for network programming and recommended to use asynchronous network programming with RunLoop. Use background thread (Grand Central Dispatch Concurrent Queue) for thread-safe data processing, not for network programming.
By the way, Blocks and Grand Central Dispatch sessions are also worth to see.
WWDC 2010 Session 206 - Introducing Blocks and Grand Central Dispatch on iPhone
WWDC 2010 Session 211 - Simplifying iPhone App Development with Grand Central Dispatch
I wrote a wrapper class for asynchronous NSURLConnection.
AsyncURLConnection.h
#import <Foundation/Foundation.h>
typedef void (^completeBlock_t)(NSData *data);
typedef void (^errorBlock_t)(NSError *error);
#interface AsyncURLConnection : NSObject
{
NSMutableData *data_;
completeBlock_t completeBlock_;
errorBlock_t errorBlock_;
}
+ (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock;
#end
AsyncURLConnection.m
#import "AsyncURLConnection.h"
#implementation AsyncURLConnection
+ (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{
return [[[self alloc] initWithRequest:requestUrl
completeBlock:completeBlock errorBlock:errorBlock] autorelease];
}
- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock
{
if ((self=[super init])) {
data_ = [[NSMutableData alloc] init];
completeBlock_ = [completeBlock copy];
errorBlock_ = [errorBlock copy];
NSURL *url = [NSURL URLWithString:requestUrl];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
return self;
}
- (void)dealloc
{
[data_ release];
[completeBlock_ release];
[errorBlock_ release];
[super dealloc];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[data_ setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[data_ appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
completeBlock_(data_);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
errorBlock_(error);
}
#end
How to use AsyncURLConnection class.
/*
* AsyncURLConnection -request:completeBlock:errorBlock: have to be called
* from Main Thread because it is required to use asynchronous API with RunLoop.
*/
[AsyncURLConnection request:url completeBlock:^(NSData *data) {
/* success! */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* process downloaded data in Concurrent Queue */
dispatch_async(dispatch_get_main_queue(), ^{
/* update UI on Main Thread */
});
});
} errorBlock:^(NSError *error) {
/* error! */
}];
Create a concurrent NSOperation on which you run your asynchronous NSURLConnection.
Have a look at this code block:
-(void)getDataFromServer
{
NSDictionary *dicParams = [NSDictionary dictionaryWithObjectsAndKeys:
userId, kUserID,
pageIndex,kPageIndex,
nil];
NSString *yourURL = [self addQueryStringToUrlString:[NSString stringWithFormat:#"%#/%#",_PATH_,apiName] withDictionary:dicParams];
NSString *queue_id = #"_queue_id_";
dispatch_queue_t queue = dispatch_queue_create([queue_id UTF8String], 0);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:yourURL]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60.0];
[theRequest setHTTPMethod:#"GET"];
[theRequest setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[theRequest setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSError *serviceError = nil;
NSURLResponse *serviceResponse = nil;
NSData *dataResponse = [NSURLConnection sendSynchronousRequest:theRequest
returningResponse:&serviceResponse
error:&serviceError];
if(serviceError)
{
dispatch_sync(main, ^{
// Update your UI
if(serviceError.code == -1012){
// Log error
}else{
// Log error
}
});
}
else
{
NSError *jsonError = nil;
id dataObject = [NSJSONSerialization JSONObjectWithData:dataResponse
options:kNilOptions
error:&jsonError];
NSMutableArray *arrResponse = (NSMutableArray *)dataObject;
dispatch_sync(main, ^{
// Update your UI
[yourTableView reloadData];
});
}
});
}
+(NSString*)addQueryStringToUrlString:(NSString *)urlString withDictionary:(NSDictionary *)dictionary
{
NSMutableString *urlWithQuerystring = [[NSMutableString alloc] initWithString:urlString];
for (id key in dictionary) {
NSString *keyString = [key description];
NSString *valueString = [[dictionary objectForKey:key] description];
if ([urlWithQuerystring rangeOfString:#"?"].location == NSNotFound) {
[urlWithQuerystring appendFormat:#"?%#=%#", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
} else {
[urlWithQuerystring appendFormat:#"&%#=%#", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
}
}
return urlWithQuerystring;
}
+(NSString*)urlEscapeString:(NSString *)unencodedString
{
CFStringRef originalStringRef = (__bridge_retained CFStringRef)unencodedString;
NSString *s = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,originalStringRef, NULL, NULL,kCFStringEncodingUTF8);
CFRelease(originalStringRef);
return s;
}

Sharing data between two classes

this is my first iPhone application and I'm using JSON framework to decode JSON sent from a server.
I insert the data in a NSMutableArray from an AppDelegate file.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
responseData = [[NSMutableData data] retain];
museums = [[NSMutableArray alloc] init];
viewController = [[RootViewController alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://my_json_link"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Connection failed: %#", [error description]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
NSDictionary *data = (NSDictionary *)[json objectWithString:responseString error:&error];
[responseString release];
if (data == nil)
NSLog(#"JSON parsing failed: %#", [error localizedDescription]);
else {
for (NSDictionary *item in data) {
// Read the data
NSString *aName = [item objectForKey:#"name"];
NSString *aDescription = [item objectForKey:#"description"];
// Create a new museum object with the data from json
Museum *museum = [[Museum alloc] initWithName:aName description:aDescription];
// Add the museum object to the Array
[museums addObject:museum];
[museum release];
}
}
viewController.museums = museums;
}
The museums array is not empty inside connectionDidFinishLoading function, but I can't see it when I try to print it in RootViewController.
I tried to set the array in the line
viewController.museums = museums;
but I didn't understand what is wrong.
I can fix the problem only if I move these lines:
[window addSubview:viewController.view];
[window makeKeyAndVisible];
from the first function to connectionDidFinishLoading function. But in this case doesn't work the other view when I click one record of the table.
Thanks for any help.
viewController = [[RootViewController alloc] init];
First, I need you to make sure that the viewController you create in didFinishLaunching... is actually the correct viewController. Have you actually wired up that instance to be what you think it is or do you two instance os RootViewController?
Second if you are actually setting museums on the right instance of RootViewController you need to make sure that your timing is correct. This means that are you setting museums BEFORE you trying to print it out in viewController
--Edit--
OK since we established that things are happening in the wrong order you should try and reload the table. The UITableView has a method called reloadData that will take care of this for you and you need to call this everytime you change the data source after the table has been created.
So in RootViewController add a method called reload which in turn calls reloadData on your UITableView and modify your code:
viewController.museums = museums;
[viewController reload];
You could add to your view controller, temporarily just for debugging:
-(void) setMuseums:(NSMutableArray*)m {
self->_museums = [m retain];
}
and then add a breakpoint in there. Make sure it's getting hit, or maybe there's something later coming along and setting it to nil.
The Museums property is declared as #property (nonatomic, retain) NSMutableArray *museums; right?
Try to put a NSLog inside the for loop and check if it is executed.
try using
viewController.museums = [museums retain];