reloadData Crashing iOS App - objective-c

I'm doing a Twitter request for API data with:
TWRequest *postRequest = [[TWRequest alloc] initWithURL:[NSURL URLWithString:#"http://search.twitter.com/search.json?q=a2zwedding&include_entities=true"] parameters:nil requestMethod:TWRequestMethodGET];
then I'm getting all processing the request with:
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
// NSString *output;
NSArray *results;
if ([urlResponse statusCode] == 200) {
NSError *jsonParsingError = nil;
NSDictionary *publicTimeline = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonParsingError];
results = [publicTimeline objectForKey:#"results"];
}
[self performSelectorOnMainThread:#selector(populateTable:) withObject:results waitUntilDone:YES];
}];
I'm then trying to display the "results" in a UITableView. My delegate and datasource are the same view controller that is processing the JSON data. My datasource method of:
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
is returning zero if I try to do the count of my array because my JSON parsing isn't finished when this gets called. If I "return 1" with this method, it properly displays one of the results from my Twitter request. However, if I use reloadData my app crashes. I cannot get it to delay the count. Any ideas?

in .h
//use this as your datasource
#property(nonatomic, strong)NSArray *myResultData;
.m
#synthesize myResultData;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
// NSString *output;
NSArray *results;
if ([urlResponse statusCode] == 200) {
NSError *jsonParsingError = nil;
NSDictionary *publicTimeline = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonParsingError];
results = [publicTimeline objectForKey:#"results"];
if(![results isKindOfClass:[NSNull class]])
{
myResultData = [NSArray alloc]initWithArray:results];
}
[self.tableView reloadData];
}
}];

Related

How to show JSON data in UIView labels

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];
});
}
}];
}

Loading Core Data From Large JSON Causing App To Crash

I'm attempting to populate CoreData from a JSON file that consists of 170,000 plus dictionaries. The parsing of the json goes quick but when I start trying to add to CoreData I'm blocking the main thread for a long time and then the app eventually crashes. It crashes when calling the method [UIDocument saveToUrl:forSaveOperation:completionHandler] Here is my code. If anyone has an idea of what's causing it to crash or a more efficient way to load CoreData that would be greatly appreciated.
#property (nonatomic, strong) UIManagedDocument *wordDatabase;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.wordDatabase) {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"Word Database"];
self.wordDatabase = [[UIManagedDocument alloc] initWithFileURL:url];
}
}
- (void)setWordDatabase:(UIManagedDocument *)wordDatabase
{
if (_wordDatabase != wordDatabase) {
_wordDatabase = wordDatabase;
[self useDocument];
}
}
- (void)useDocument
{
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.wordDatabase.fileURL path]]) {
// does not exist on disk, so create it
[self.wordDatabase saveToURL:self.wordDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
[self setupFetchedResultsController];
[self prepopulateWordDatabaseWithDocument:self.wordDatabase];
}];
}
}
- (void)prepopulateWordDatabaseWithDocument:(UIManagedDocument *)document
{
dispatch_queue_t fetchQ = dispatch_queue_create("Word Fetcher", NULL);
dispatch_async(fetchQ, ^{
//Fetch the words from the json file
NSString *fileString = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"json"];
NSString *jsonString = [[NSString alloc] initWithContentsOfFile:fileString encoding:NSUTF8StringEncoding error: NULL];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSArray *words = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
[document.managedObjectContext performBlock:^{
for (NSDictionary *dictionary in words)
{
[Word wordFromDictionary:dictionary inManagedObjectContext:document.managedObjectContext];
}
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
}];
});
dispatch_release(fetchQ);
}
What I ended up doing that stopped my app from crashing was allocating a new NSManagedObjectContext and peformed all my loading in the background. After saving I called my NSFetchedResultsController and the table repopulated.
- (void)prepopulateWordDatabaseWithDocument:(UIManagedDocument *)document
{
NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.undoManager = nil;
backgroundContext.persistentStoreCoordinator = document.managedObjectContext.persistentStoreCoordinator;
[backgroundContext performBlock:^{
NSString *fileString = [[NSBundle mainBundle] pathForResource:#"words" ofType:#"json"];
NSString *jsonString = [[NSString alloc] initWithContentsOfFile:fileString encoding:NSUTF8StringEncoding error: NULL];
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *parseError;
NSArray *words = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&parseError];
for (NSDictionary *dictionary in words)
{
[Word wordFromDictionary:dictionary inManagedObjectContext:backgroundContext];
}
NSError *loadError;
if ([backgroundContext save:&loadError]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setupFetchedResultsController];
});
}
}];
}

How do I loop through tweets to access geo information and add to an array

How would I loop through the JSON returned by a TWRequest to get the geo information of a tweet? I am using the code below - I have marked up the bit I am unsure about. the text component works fine, I'm just not sure how to create the array of geo data and access this...
- (void)fetchTweets
{
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
//NSLog(#"phrase carried over is %#", delegate.a);
// Do a simple search, using the Twitter API
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:
[NSString stringWithFormat:#"http://search.twitter.com/search.json?q=%#", delegate.a]]
parameters:nil requestMethod:TWRequestMethodGET];
// Notice this is a block, it is the handler to process the response
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if ([urlResponse statusCode] == 200)
{
// The response from Twitter is in JSON format
// Move the response into a dictionary and print
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
//NSLog(#"Twitter response: %#", dict);
NSArray *results = [dict objectForKey:#"results"];
//Loop through the results
for (NSDictionary *tweet in results) {
// Get the tweet
NSString *twittext = [tweet objectForKey:#"text"];
//added this one - need to check id NSString is ok??
NSString *twitlocation = [tweet objectForKey:#"geo"];
// Save the tweet to the twitterText array
[_twitterText addObject:twittext];
//this is the loop for the location
[twitterLocation addObject:twitlocation];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
}
"geo" is deprecated and probably not filled at all. I far as I remember it was deprecated in Twitter API v1.0 too. Try this code:
- (void)fetchTweets
{
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication]delegate];
//NSLog(#"phrase carried over is %#", delegate.a);
// Do a simple search, using the Twitter API
TWRequest *request = [[TWRequest alloc] initWithURL:[NSURL URLWithString:
[NSString stringWithFormat:#"http://search.twitter.com/search.json?q=%#", delegate.a]]
parameters:nil requestMethod:TWRequestMethodGET];
// Notice this is a block, it is the handler to process the response
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if ([urlResponse statusCode] == 200)
{
// The response from Twitter is in JSON format
// Move the response into a dictionary and print
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
//NSLog(#"Twitter response: %#", dict);
NSArray *results = [dict objectForKey:#"results"];
//Loop through the results
for (NSDictionary *tweet in results) {
// Get the tweet
NSString *twittext = [tweet objectForKey:#"text"];
//added this one - need to check id NSString is ok??
id jsonResult = [tweet valueForKeyPath:#"coordinates.coordinates"];
if ([NSNull null] != jsonResult) {
if (2 == [jsonResult count]) {
NSDecimalNumber* longitude = [jsonResult objectAtIndex:0];
NSDecimalNumber* latitude = [jsonResult objectAtIndex:1];
if (longitude && latitude) {
// here you have your coordinates do whatever you like
[twitterLocation addObject:[NSString stringWithFormat:#"%#,%#", latitude, longitude]];
}
else {
NSLog(#"Warning: bad coordinates: %#", jsonResult);
}
}
else {
NSLog(#"Warning: bad coordinates: %#", jsonResult);
}
}
// Save the tweet to the twitterText array
[_twitterText addObject:twittext];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
}

Returning NSDictionary from async code block? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
returning UIImage from block
Hi I'm trying to return dictionary of json twitter data so i can use it in my application. How ever it is being called from a async block. I can not save it or return it any thoughts?
-(NSDictionary *)TweetFetcher
{
TWRequest *request = [[TWRequest alloc] initWithURL:
[NSURL URLWithString: #"http://search.twitter.com/search.json?
q=iOS%205&rpp=5&with_twitter_user_id=true&result_type=recent"] parameters:nil
requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse
*urlResponse,
NSError *error)
{
if ([urlResponse statusCode] == 200)
{
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData
options:0 error:&error];
//resultsArray return an array [of dicitionaries<tweets>];
NSArray* resultsArray = [dict objectForKey:#"results"];
for (NSDictionary* internalDict in resultsArray)
NSLog([NSString stringWithFormat:#"%#", [internalDict
objectForKey:#"from_user_name"]]);
----> return dict; // i need this dictionary of json twitter data
}
else
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
}];
}
Thnx in advance!
I feel like I've written a ton of this async code lately.
- (void)tweetFetcherWithCompletion:(void(^)(NSDictionary *dict, NSError *error))completion
{
NSURL *URL = [NSURL URLWithString:#"http://search.twitter.com/search.json?q=iOS%205&rpp=5&with_twitter_user_id=true&result_type=recent"];
TWRequest *request = [[TWRequest alloc] initWithURL:URL parameters:nil requestMethod:TWRequestMethodGET];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if ([urlResponse statusCode] == 200) {
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
if (error) {
completion(nil, error);
return;
}
//resultsArray return an array [of dicitionaries<tweets>];
NSArray* resultsArray = [dict objectForKey:#"results"];
for (NSDictionary* internalDict in resultsArray)
NSLog(#"%#", [internalDict objectForKey:#"from_user_name"]);
completion(dict, nil);
}
else {
NSLog(#"Twitter error, HTTP response: %i", [urlResponse statusCode]);
completion(nil, error);
}
}];
}
So, instead of calling self.tweetDict = [self TweetFetcher];, you would call it this way.
[self tweetFetcherWithCompletion:^(NSDictionary *dict, NSError *error) {
if (error) {
// Handle Error Somehow
}
self.tweetDict = dict;
// Everything else you need to do with the dictionary.
}];

ASIFormDataRequest in AFNetworking?

I have some code in ASIHTTP, but I want to move on AFNetworking.
I used ASIFormDataRequest for some POST requests and this code works fine:
NSURL *url = [NSURL URLWithString:#"http://someapiurl"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:#"123" forKey:#"phone_number"];
[request startSynchronous];
NSError *error = [request error];
if (!error) {
NSLog(#"Response: %#", [[request responseString] objectFromJSONString]);
}
but, when I tried to do the same with AFNetworking, I got in problem with content-type (I guess).
This is AFNetworking code, and it doesn't work:
NSURL *url = [NSURL URLWithString:#"http://dev.url"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"123", #"phone_number",
nil];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:#"/api/get_archive" parameters:params];
[request setValue:#"application/x-www-form-urlencoded; charset=UTF8" forHTTPHeaderField:#"Content-Type"];
AFHTTPRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest
*request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"Response: %#", JSON);
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
NSLog(#"Error: %#", error);
}];
[operation start];
URL is fine, this is checked.
I'm getting from server this:
{NSErrorFailingURLKey=http://dev.thisapiurl, NSLocalizedDescription=Expected content type {(
"text/json",
"application/json",
"text/javascript"
)}, got text/html}
The problem you're having is because you are instantiating an AFJSONRequestOperation, which by default expects a JSON-friendly response type. Are you expecting a JSON response? If not, you should use a less-specific Request class. For example, you could use HTTPRequestOperationWithRequest: .
NSURL *url = [NSURL URLWithString:#"http://dev.url"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"123", #"phone_number",
nil];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"POST" path:#"/api/get_archive" parameters:params];
[request setValue:#"application/x-www-form-urlencoded; charset=UTF8" forHTTPHeaderField:#"Content-Type"];
//Notice the different method here!
AFHTTPRequestOperation *operation = [httpClient HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Response: %#", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"Error: %#", error);
}];
//Enqueue it instead of just starting it.
[httpClient enqueueHTTPRequestOperation:operation];
If you have more specific request/response types (JSON, XML, etc), you can use those specific AFHTTPRequestOperation subclasses. Otherwise, just use the vanilla HTTP one.
I recently went through the same thing as you. Here is a custom class I wrote to handle pretty much any network request.
NetworkClient.h:
//
// NetworkClient.h
//
// Created by LJ Wilson on 3/8/12.
// Copyright (c) 2012 LJ Wilson. All rights reserved.
//
#import <Foundation/Foundation.h>
extern NSString * const ACHAPIKey;
#interface NetworkClient : NSObject
+(void)processURLRequestWithURL:(NSString *)url
andParams:(NSDictionary *)params
block:(void (^)(id obj))block;
+(void)processURLRequestWithURL:(NSString *)url
andParams:(NSDictionary *)params
syncRequest:(BOOL)syncRequest
block:(void (^)(id obj))block;
+(void)processURLRequestWithURL:(NSString *)url
andParams:(NSDictionary *)params
syncRequest:(BOOL)syncRequest
alertUserOnFailure:(BOOL)alertUserOnFailure
block:(void (^)(id obj))block;
+(void)handleNetworkErrorWithError:(NSError *)error;
+(void)handleNoAccessWithReason:(NSString *)reason;
#end
NetworkClient.m:
//
// NetworkClient.m
//
// Created by LJ Wilson on 3/8/12.
// Copyright (c) 2012 LJ Wilson. All rights reserved.
//
#import "NetworkClient.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#import "SBJson.h"
NSString * const APIKey = #"APIKeyIfYouSoDesire";
#implementation NetworkClient
+(void)processURLRequestWithURL:(NSString *)url
andParams:(NSDictionary *)params
block:(void (^)(id obj))block {
[self processURLRequestWithURL:url andParams:params syncRequest:NO alertUserOnFailure:NO block:^(id obj) {
block(obj);
}];
}
+(void)processURLRequestWithURL:(NSString *)url
andParams:(NSDictionary *)params
syncRequest:(BOOL)syncRequest
block:(void (^)(id obj))block {
if (syncRequest) {
[self processURLRequestWithURL:url andParams:params syncRequest:YES alertUserOnFailure:NO block:^(id obj) {
block(obj);
}];
} else {
[self processURLRequestWithURL:url andParams:params syncRequest:NO alertUserOnFailure:NO block:^(id obj) {
block(obj);
}];
}
}
+(void)processURLRequestWithURL:(NSString *)url
andParams:(NSDictionary *)params
syncRequest:(BOOL)syncRequest
alertUserOnFailure:(BOOL)alertUserOnFailure
block:(void (^)(id obj))block {
// Default url goes here, pass in a nil to use it
if (url == nil) {
url = #"MyDefaultURLGoesHere";
}
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:params];
[dict setValue:APIKey forKey:#"APIKey"];
NSDictionary *newParams = [[NSDictionary alloc] initWithDictionary:dict];
NSURL *requestURL;
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:requestURL];
NSMutableURLRequest *theRequest = [httpClient requestWithMethod:#"POST" path:url parameters:newParams];
__block NSString *responseString = [NSString stringWithString:#""];
AFHTTPRequestOperation *_operation = [[AFHTTPRequestOperation alloc] initWithRequest:theRequest];
__weak AFHTTPRequestOperation *operation = _operation;
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
responseString = [operation responseString];
id retObj = [responseString JSONValue];
// Check for invalid response (No Access)
if ([retObj isKindOfClass:[NSDictionary class]]) {
if ([[(NSDictionary *)retObj valueForKey:#"Message"] isEqualToString:#"No Access"]) {
block(nil);
[self handleNoAccessWithReason:[(NSDictionary *)retObj valueForKey:#"Reason"]];
}
} else if ([retObj isKindOfClass:[NSArray class]]) {
NSDictionary *dict = [(NSArray *)retObj objectAtIndex:0];
if ([[dict valueForKey:#"Message"] isEqualToString:#"No Access"]) {
block(nil);
[self handleNoAccessWithReason:[(NSDictionary *)retObj valueForKey:#"Reason"]];
}
}
block(retObj);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failed with error = %#", [NSString stringWithFormat:#"[Error]:%#",error]);
block(nil);
if (alertUserOnFailure) {
[self handleNetworkErrorWithError:operation.error];
}
}];
[operation start];
if (syncRequest) {
// Only fires if Syncronous was passed in as YES. Default is NO
[operation waitUntilFinished];
}
}
+(void)handleNetworkErrorWithError:(NSError *)error {
NSString *errorString = [NSString stringWithFormat:#"[Error]:%#",error];
// Standard UIAlert Syntax
UIAlertView *myAlert = [[UIAlertView alloc]
initWithTitle:#"Connection Error"
message:errorString
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[myAlert show];
}
+(void)handleNoAccessWithReason:(NSString *)reason {
// Standard UIAlert Syntax
UIAlertView *myAlert = [[UIAlertView alloc]
initWithTitle:#"No Access"
message:reason
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[myAlert show];
}
#end
This adds in a couple of features you may not need or want, feel free to modify it as you need as long as the Copyright section stays in place. I use that APIKey to validate the request came from my app and not someone trying to hack things.
Calling it (assuming you have included NetworkClient.h:
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"ParamValue1", #"ParamName1",
#"ParamValue2", #"ParamName2",
nil];
[NetworkClient processURLRequestWithURL:nil andParams:params block:^(id obj) {
if ([obj isKindOfClass:[NSArray class]]) {
// Do whatever you want with the object. In this case, I knew I was expecting an Array, but it will return a Dictionary if that is what the web-service responds with.
}
}];
Also can:
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"ParamValue1", #"ParamName1",
nil];
NSString *urlString = #"https://SuppliedURLOverridesDefault";
[NetworkClient processURLRequestWithURL:urlString
andParams:params
syncRequest:YES
alertUserOnFailure:NO
block:^(id obj) {
if ([obj isKindOfClass:[NSArray class]]) {
// Do stuff
}
}];
So it will take in any number of parameters, inject an APIKey or anything else if you want and return back either a Dictionary or an Array depending on the web-service. This does expect SBJson BTW.