i've made following Asynchronous request, the problem is that its empty i've tried in the bottom NSLog the fixtures where its empty. I've checked that the nsstring home, away, league and so on returns values and it does. How come the values are not added to the fixtures NSMutableArray
[ProgressHUD show:#"Loading..."];
NSURL *url = [NSURL URLWithString:#"API_URL"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
jsonResult = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
int subObjects = ((NSArray *)jsonResult[#"match"]).count;
for (int i = 0; i <= subObjects-1; i++) {
NSString *date = [NSString stringWithFormat:#"%# %#",[[[jsonResult valueForKey:#"match"] valueForKey:#"playdate"] objectAtIndex:i], [[[jsonResult valueForKey:#"match"] valueForKey:#"time"] objectAtIndex:i]];
NSString *identifier = [[NSLocale currentLocale] localeIdentifier];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setTimeZone: [NSTimeZone timeZoneWithName:#"US/Arizona"]];
[df setLocale:[NSLocale localeWithLocaleIdentifier:identifier]];
[df setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *myDate = [df dateFromString:[NSString stringWithFormat:#"%#", date]];
NSArray *items = [[NSString stringWithFormat:#"%#", myDate] componentsSeparatedByString:#" "];
NSString *home = [[[jsonResult valueForKey:#"match"] valueForKey:#"hometeam"] objectAtIndex:i];
NSString *away = [[[jsonResult valueForKey:#"match"] valueForKey:#"awayteam"] objectAtIndex:i];
NSString *league = [[[jsonResult valueForKey:#"match"] valueForKey:#"league"] objectAtIndex:i];
[fixtures addObject:
[[NSMutableDictionary alloc] initWithObjectsAndKeys:
items[0], #"date",
items[1], #"time",
home, #"home",
away, #"away",
league, #"league",
nil]];
[sections addObject:
[[NSMutableDictionary alloc] initWithObjectsAndKeys:
items[0], #"date",
nil]];
}
}
];
[self.theTableView reloadData];
[ProgressHUD dismiss];
NSLog(#"%#", fixtures);
The problem is that the request is an asynchronous function
If the function is asynchronous, the function will create another thread and return immediately to execute the next line after the one that invoked the asynchronous function. Meanwhile the new thread will execute some code and, eventually execute the block passed as parameter, and finally the thread is killed and doesn't exist any more.
This means that
NSLog(#"%#", fixtures);
will most likely be executed before the sendAsynchronousRequest has finished it's job, that's why it is returning nil.
Everything you need to do to process the downloaded information should happen inside the completionHandler block, including the call to [self.theTableView reloadData];
It is a non-blocking operation. It means that by calling this method it returns immediatelly while the actual request is performing somewhere in background and then calls the handler block on queue specified in queue parameter.
You should reload tableview from the completion-handler block.
// 1 before request
NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
// 3 request completed
// some processing
...
[self.theTableView reloadData];
[ProgressHUD dismiss];
}
// 2 immediate return
update
Although you passing the main queue as queue parameter the handler block will be performed on next run loop iteration after you reloading table and logging the values.
// current run loop iteration
NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
// next run loop iteration
}
// current run loop iteration
if(!fixtures) {
fixtures = [[NSMutableArray alloc] init];
}
[fixtures addObject:#{
#"date": items[0],
#"time": items[1],
#"home": home,
#"away": away,
#"league": league
}];
if(!sections) {
sections = [[NSMutableArray alloc] init];
}
[sections addObject:#{
#"date": items[0]
}];
[self.theTableView reloadData];
[ProgressHUD dismiss];
NSLog(#"%#", fixtures);
Related
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.
I'm stuck with an issue on how to best load a couple of UILabels asynchronously.
Here is my cellForRowAtIndexPath method:
UPDATE:
Based on the answer below, I've made changes:
Here is my new cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Retrieve cell
NSString *cellIdentifier = #"BuildingItem";
BuildingsTableViewCell *cell = [[tableView dequeueReusableCellWithIdentifier:cellIdentifier] initWithFrame:CGRectMake(10, 10,580, 100)];
// Get the area to be shown
Buildings *item = _feedItems[indexPath.row];
NSURL *MyURL = [NSURL URLWithString:item.buildingPointerImage];
UIImage *placeholder = [UIImage imageNamed:#"placeholder"];
NSString *path = [MyURL absoluteString];
NSString *key = [path MD5Hash];
[cell.buildingImageView loadImageFromURL:(NSURL*)MyURL placeholderImage:(UIImage*)placeholder cachingKey:(NSString*)key];
cell.buildingName.text = item.buildingPointerName;
cell.buildingYear.text = item.buildingPointerYear;
NSString *URL = #"http://rets.miamiresidential.com/ios/condos/buildings.php?action=get_range";
NSString *streetNumberURL = [NSString stringWithFormat:#"&street_number=%#",item.buildingStreetNumber];
NSString *streetNameURL = [NSString stringWithFormat:#"&street_name=%#",item.buildingStreetName];
NSString *ZipcodeURL = [NSString stringWithFormat:#"&zipcode=%#",item.buildingZipcode];
NSString *P1 = [URL stringByAppendingString:streetNumberURL];
NSString *P2 = [P1 stringByAppendingString:streetNameURL];
NSString *P3 = [P2 stringByAppendingString:ZipcodeURL];
NSURL *jsonFileUrl = [NSURL URLWithString:P3];
NSLog(#"%#",P3);
[cell.buildingSalesRange loadSalesRangeFromURL:(NSURL*)jsonFileUrl];
[cell.buildingRentalsRange loadRentalsRangeFromURL:(NSURL*)jsonFileUrl];
cell.layer.borderColor = [[UIColor whiteColor]CGColor];
cell.layer.backgroundColor = [[UIColor clearColor]CGColor];
cell.layer.borderWidth = 2;
return cell;
}
and here is my new .m file:
#import "PriceRanges.h"
#import <objc/runtime.h>
#implementation UILabel(Prices)
-(void) loadSalesRangeFromURL:(NSURL*)url {
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
NSNumberFormatter *currencyStyle = [[NSNumberFormatter alloc] init];
[currencyStyle setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyStyle setLocale:locale];
[currencyStyle setMaximumFractionDigits:0];
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:url];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if (error)
{
NSLog(#"Error,%#", [error localizedDescription]);
}
else
{
NSArray *priceArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
NSNumber *number = [priceArray valueForKeyPath:#"sales.number"];
NSString *lowest = [priceArray valueForKeyPath:#"sales.lowest"];
NSDecimalNumber *lowestDecimal = [NSDecimalNumber decimalNumberWithString:lowest];
NSString *lowestPrice = [currencyStyle stringFromNumber:lowestDecimal];
if (![number isEqualToNumber:[NSNumber numberWithInt:0]]) {
dispatch_async(dispatch_get_main_queue(), ^{
UILabel *labelFromData = [[UILabel alloc] init];
[labelFromData setText:[NSString stringWithFormat:#"%# for Sale from %#",number,lowestPrice]];
if (labelFromData) {
if ([self.text isEqualToString:labelFromData.text]) {
} else {
dispatch_async(dispatch_get_main_queue(), ^{
self.text = labelFromData.text;
});
}
}
self.text = [NSString stringWithFormat:#"%# for Sale from %#",number,lowestPrice];
});
}
};
}];
}
-(void) loadRentalsRangeFromURL:(NSURL*)url {
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
NSNumberFormatter *currencyStyle = [[NSNumberFormatter alloc] init];
[currencyStyle setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyStyle setLocale:locale];
[currencyStyle setMaximumFractionDigits:0];
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:url];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if (error)
{
NSLog(#"Error,%#", [error localizedDescription]);
}
else
{
NSArray *priceArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
NSNumber *number = [priceArray valueForKeyPath:#"rentals.number"];
NSString *lowest = [priceArray valueForKeyPath:#"rentals.lowest"];
NSDecimalNumber *lowestDecimal = [NSDecimalNumber decimalNumberWithString:lowest];
NSString *lowestPrice = [currencyStyle stringFromNumber:lowestDecimal];
if (![number isEqualToNumber:[NSNumber numberWithInt:0]]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self setText:[NSString stringWithFormat:#"%# for Rent from %#",number,lowestPrice]];
});
}
}
}];
}
#end
As you might see, I have two different methods, but both still send the async request everytime the tables are scrolled.
I guess I am not clear on what to do now?
That's the problem with not having a data model independent of your UI, and a good example of when Model-View-Controller makes sense. You'll need another layer (preferably a separate class) responsible for keeping all the data you've fetched from the network, deciding when it's old enough to discard, etc. The cells should populate themselves from the cached data that the Model keeps, and if the data's not yet present, the model fetches it asynchronously and then notifies the View Controller when the new data arrive. If the cells are still visible, they get populated. If they've scrolled offscreen, been reused, etc, then the data updates don't result in any immediate UI change.
Hi good people I'm trying to prevent the freezing with
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{ CODE });
but I don't know how to use function.. I don't know where to put the managedObjectContext and how to use this dispatch_async my code is:
- (void)updateFacebookFriendsHighScore{
NSFetchRequest *requestche =[NSFetchRequest fetchRequestWithEntityName:#"Time"];
[requestche setReturnsObjectsAsFaults:NO];
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"timeid==1"];
requestche.predicate=predicate;
NSArray *getIDTime = [self.managedObjectContext executeFetchRequest:requestche error:nil];
NSString *getTheTime = [[getIDTime valueForKey:#"time"] componentsJoinedByString:#""];
NSNumber *timeInInt = [NSNumber numberWithInteger: [getTheTime intValue]];
int timeFromDB = [timeInInt intValue];
timeFromDB = timeFromDB + 509;
int timeNow = [[NSDate date] timeIntervalSince1970];
if(timeNow > timeFromDB){
NSFetchRequest *updateHighScoreRequest = [NSFetchRequest fetchRequestWithEntityName:#"Friends"];
[updateHighScoreRequest setReturnsObjectsAsFaults:NO];
NSArray *friendsToUpdate = [self.managedObjectContext executeFetchRequest:updateHighScoreRequest error:nil];
for(NSArray *friendId in friendsToUpdate){
NSString *getFriendId = [friendId valueForKey:#"fbid"] ;
NSString *siteURL = [NSString stringWithFormat:#"http://www.example.com/example.php?fbid=%#", getFriendId];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:siteURL]];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSString *resultsFromDB = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSNumber *theScoreForUpdate = [NSNumber numberWithInt:[resultsFromDB intValue]];
NSFetchRequest *updateTheHighScoreRequest = [NSFetchRequest fetchRequestWithEntityName:#"Friends"];
NSPredicate *updateTheHighScorePredicate = [NSPredicate predicateWithFormat:#"fbid==%#",getFriendId];
updateTheHighScoreRequest.predicate=updateTheHighScorePredicate;
Friends *setScore = [[self.managedObjectContext executeFetchRequest:updateTheHighScoreRequest error:nil] lastObject];
NSLog(#"%#", setScore);
[setScore setValue:theScoreForUpdate forKey:#"score"];
[self.managedObjectContext save:nil];
data = nil;
resultsFromDB = nil;
theScoreForUpdate = nil;
setScore = nil;
}];
updateHighScoreRequest = nil;
}
}
requestche = nil;
}
This code gets the time from database and update the highscore after 509 seconds from the CD result and when I run this request my app freeze ( DEADLOCK ).
I am from Bulgaria and I'm trying to learn Objective C. Here we don't have schools for this our country is very bad in all instance and Bulgaria is last in Europe Union...
Can some serious and good person help me with my code or explane how works everything in Objective C or only help me with this ?
Try this code. To keep the application as simple as possible, never take the Core data code out of the main thread i.e. any thing related to self.managedObjectContext such as save or executing fetch requests. It is because Core data is not thread safe and you will have to device a strategy to handle that. I am assuming that your application is straight forward and you don't need such a strategy. So, please try to keep it as simple as possible and always perform the core data operations (save, execute) on main thread. dispatch_async(dispatch_get_main_queue(), ^{ code }); will execute it on main thread.
-(void) updateFacebookFriendsHighScore
{
dispatch_async(dispatch_get_main_queue(), ^{
NSFetchRequest *requestche =[NSFetchRequest fetchRequestWithEntityName:#"Time"];
[requestche setReturnsObjectsAsFaults:NO];
NSPredicate *predicate=[NSPredicate predicateWithFormat:#"timeid==1"];
requestche.predicate=predicate;
NSArray *getIDTime = [self.managedObjectContext executeFetchRequest:requestche error:nil];
NSString *getTheTime = [[getIDTime valueForKey:#"time"] componentsJoinedByString:#""];
NSNumber *timeInInt = [NSNumber numberWithInteger: [getTheTime intValue]];
int timeFromDB = [timeInInt intValue];
timeFromDB = timeFromDB + 509;
int timeNow = [[NSDate date] timeIntervalSince1970];
if(timeNow > timeFromDB){
NSFetchRequest *updateHighScoreRequest = [NSFetchRequest fetchRequestWithEntityName:#"Friends"];
[updateHighScoreRequest setReturnsObjectsAsFaults:NO];
NSArray *friendsToUpdate = [self.managedObjectContext executeFetchRequest:updateHighScoreRequest error:nil];
for(NSArray *friendId in friendsToUpdate){
NSString *getFriendId = [friendId valueForKey:#"fbid"] ;
NSString *siteURL = [NSString stringWithFormat:#"http://www.example.com/example.php?fbid=%#", getFriendId];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:siteURL]];
[NSURLConnection sendAsynchronousRequest:urlRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSString *resultsFromDB = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSNumber *theScoreForUpdate = [NSNumber numberWithInt:[resultsFromDB intValue]];
NSFetchRequest *updateTheHighScoreRequest = [NSFetchRequest fetchRequestWithEntityName:#"Friends"];
NSPredicate *updateTheHighScorePredicate = [NSPredicate predicateWithFormat:#"fbid==%#",getFriendId];
updateTheHighScoreRequest.predicate=updateTheHighScorePredicate;
Friends *setScore = [[self.managedObjectContext executeFetchRequest:updateTheHighScoreRequest error:nil] lastObject];
NSLog(#"%#", setScore);
[setScore setValue:theScoreForUpdate forKey:#"score"];
[self.managedObjectContext save:nil];
data = nil;
resultsFromDB = nil;
theScoreForUpdate = nil;
setScore = nil;
}];
updateHighScoreRequest = nil;
}
}
requestche = nil;
});
}
My app parses information from a Rails app using JSON. I'm looking for a way to load the JSON asynchronously, but I'm having trouble getting my code to work with examples I have found because of the complexity of my code. What do I have to do to make my JSON load asynchronously? Thanks.
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *upcomingReleaseURL = [NSURL URLWithString:#"http://obscure-lake-7450.herokuapp.com/upcoming.json"];
NSData *jsonData = [NSData dataWithContentsOfURL:upcomingReleaseURL];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSArray *upcomingReleasesArray = [dataDictionary objectForKey:#"upcoming_releases"];
//This is the dateFormatter we'll need to parse the release dates
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSTimeZone *est = [NSTimeZone timeZoneWithAbbreviation:#"EST"];
[dateFormatter setTimeZone:est];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]]; //A bit of an overkill to avoid bugs on different locales
//Temp array where we'll store the unsorted bucket dates
NSMutableArray *unsortedReleaseWeek = [[NSMutableArray alloc] init];
NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
for (NSDictionary *upcomingReleaseDictionary in upcomingReleasesArray) {
//We find the release date from the string
NSDate *releaseDate = [dateFormatter dateFromString:[upcomingReleaseDictionary objectForKey:#"release_date"]];
//We create a new date that ignores everything that is not the actual day (ignoring stuff like the time of the day)
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components =
[gregorian components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:releaseDate];
//This will represent our releases "bucket"
NSDate *bucket = [gregorian dateFromComponents:components];
//We get the existing objects in the bucket and update it with the latest addition
NSMutableArray *releasesInBucket = [tmpDict objectForKey:bucket];
if (!releasesInBucket){
releasesInBucket = [NSMutableArray array];
[unsortedReleaseWeek addObject:bucket];
}
UpcomingRelease *upcomingRelease = [UpcomingRelease upcomingReleaseWithName:[upcomingReleaseDictionary objectForKey:#"release_name"]];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
upcomingRelease.release_price = [upcomingReleaseDictionary objectForKey:#"release_price"];
upcomingRelease.release_colorway = [upcomingReleaseDictionary objectForKey:#"release_colorway"];
upcomingRelease.release_date = [upcomingReleaseDictionary objectForKey:#"release_date"];
upcomingRelease.thumb = [upcomingReleaseDictionary valueForKeyPath:#"thumb"];
upcomingRelease.images = [upcomingReleaseDictionary objectForKey:#"images"];
[releasesInBucket addObject:upcomingRelease];
[tmpDict setObject:releasesInBucket forKey:bucket];
}
[unsortedReleaseWeek sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSDate* date1 = obj1;
NSDate* date2 = obj2;
//This will sort the dates in ascending order (earlier dates first)
return [date1 compare:date2];
//Use [date2 compare:date1] if you want an descending order
}];
self.releaseWeekDictionary = [NSDictionary dictionaryWithDictionary:tmpDict];
self.releaseWeek = [NSArray arrayWithArray:unsortedReleaseWeek];
}
One simple approach is to use NSURLConnection's convenient class method sendAsynchronousRequest:queue:error.
The following code snippet is an example how to load a JSON from a server, and where the completion handler executes on a background thread which parses the JSON. It also performs all recommended error checking:
NSURL* url = [NSURL URLWithString:#"http://example.com"];
NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest addValue:#"application/json" forHTTPHeaderField:#"Accept"];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:queue
completionHandler:^(NSURLResponse* response,
NSData* data,
NSError* error)
{
if (data) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
// check status code and possibly MIME type (which shall start with "application/json"):
NSRange range = [response.MIMEType rangeOfString:#"application/json"];
if (httpResponse.statusCode == 200 /* OK */ && range.length != 0) {
NSError* error;
id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (jsonObject) {
dispatch_async(dispatch_get_main_queue(), ^{
// self.model = jsonObject;
NSLog(#"jsonObject: %#", jsonObject);
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//[self handleError:error];
NSLog(#"ERROR: %#", error);
});
}
}
else {
// status code indicates error, or didn't receive type of data requested
NSString* desc = [[NSString alloc] initWithFormat:#"HTTP Request failed with status code: %d (%#)",
(int)(httpResponse.statusCode),
[NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]];
NSError* error = [NSError errorWithDomain:#"HTTP Request"
code:-1000
userInfo:#{NSLocalizedDescriptionKey: desc}];
dispatch_async(dispatch_get_main_queue(), ^{
//[self handleError:error]; // execute on main thread!
NSLog(#"ERROR: %#", error);
});
}
}
else {
// request failed - error contains info about the failure
dispatch_async(dispatch_get_main_queue(), ^{
//[self handleError:error]; // execute on main thread!
NSLog(#"ERROR: %#", error);
});
}
}];
Although, it appears somewhat elaborate, IMO this is a minimalistic and still naïve approach. Among other disadvantages, the main issues are:
it lacks the possibility to cancel the request, and
there is no way to handle more sophisticated authentication.
A more sophisticated approach needs to utilize NSURLConnection delegates. Usually, third party libraries do implement it in this manner, encapsulating the a NSURLConnection request and other relevant state info into a subclass of NSOperation. You may start with your own implementation, for example using this code as a template.
If you just want to get this only json data, you do not need to set up a lot of things.
use the code below. Create jsonParse method which gets a NSData Object.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:#"http://obscure-lake-7450.herokuapp.com/upcoming.json"]];
dispatch_sync(dispatch_get_main_queue(), ^{
[self jsonParse:data];
});
});
Download your data async as in this answer: Object-c/iOS :How to use ASynchronous to get a data from URL?
Then run it through the json parser.
To generically run code in a background thread you can use this method:
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Code here is run on a background thread
dispatch_async( dispatch_get_main_queue(), ^{
// Code here is run on the main thread (the UI thread) after your code above has completed so you can update UI after the JSON call has completed if you need to.
});
});
But remember that Apple does not allow you to update UI elements in a background thread. Also, they do not allow you to spawn more threads from a background thread, it must be done from the main thread.
NSString *urlstr=#"http://itunes.apple.com/in/rss/topsongs/limit=25/json";
NSMutableURLRequest *request=[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:urlstr]];
[NSURLConnection sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse* response,
NSData* data, NSError* error)
{
NSError *myError = nil;
NSDictionary *dic1 = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&myError];
if (myError ==nil) {
NSDictionary*feed =[dic1 objectForKey:#"feed"];
NSArray*arrayofentry =[feed objectForKey:#"entry"];
for(NSDictionary *dic2 in arrayofentry) {
requestReply=[dic2 objectForKey:#"title"];
[arr1 addObject:requestReply];
}
[self.table reloadData];
}
}];
Try this code:
NSURL * inkURL = [NSURL URLWithString:#"your url"];
NSURLRequest * request = [[NSURLRequest alloc]initWithURL:inkURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];
NSOperationQueue * queue = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse * response, NSData * data, NSError * error) {
NSData * jsonData = [NSData dataWithContentsOfURL:inkURL];
NSDictionary * dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
self.inkArray = [dataDictionary objectForKey:#"users"];
}];
JUST started doing work with blocks... very confusing. I am using a block like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *myDictionary = [[mySingleton arrayPeopleAroundMe] objectAtIndex:indexPath.row];
NSMutableString *myString = [[NSMutableString alloc] initWithString:#"http://www.domain.com/4DACTION/PP_profileDetail/"];
[myString appendString:[myDictionary objectForKey:#"userID"]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[myString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:queue
completionHandler: ^( NSURLResponse *response,
NSData *data,
NSError *error)
{
[[mySingleton dictionaryUserDetail] removeAllObjects];
[[mySingleton arrayUserDetail] removeAllObjects];
if ([data length] > 0 && error == nil) // no error and received data back
{
NSError* error;
NSDictionary *myDic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
[mySingleton setDictionaryUserDetail:[myDic mutableCopy]];
NSArray *myArray = [myDic objectForKey:#"searchResults"];
[mySingleton setArrayUserDetail:[myArray mutableCopy]];
[self userDetailComplete];
} else if
([data length] == 0 && error == nil) // no error and did not receive data back
{
[self serverError];
} else if
(error != nil) // error
{
[self serverError];
}
}];
}
Once the connection is completed, this is called:
-(void)userDetailComplete {
ViewProfile *vpVC = [[ViewProfile alloc] init];
[vpVC setPassedInstructions:#"ViewDetail"];
[[self navigationController] pushViewController:vpVC animated:YES];
}
which caused this error to pop up:
"Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread."
The only way I got rid of the error was by changing userDetailComplete to this:
-(void)userDetailComplete {
dispatch_async(dispatch_get_main_queue(), ^{
ViewProfile *vpVC = [[ViewProfile alloc] init];
[vpVC setPassedInstructions:#"ViewDetail"];
[[self navigationController] pushViewController:vpVC animated:YES];
});
}
My question: is a new thread started automatically every time a block is used? Are there any other pitfalls I should aware of when using blocks?
Blocks do not create threads. They are closures; they just contain runnable code that can be run at some future point.
This is running on a background thread because that's what you asked it to do:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:queue
...
You created a new queue and then asked NSURLConnection to call you back on that queue. If you want to be called back on the main thread, pass [NSOperationQueue mainQueue]. That's usually waht you want.