im trying to show on a map two json files simulteniously. But there is the problem it gives me weird errors which I can't figure it out. So originaly there was
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray * annotations = [[NSMutableArray alloc] init];
self.mapView.visibleMapRect = MKMapRectMake(135888858.533591, 92250098.902419, 190858.927912, 145995.678292);
NSLog(#"Loading data…");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData * JSONData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:self.seedFileName ofType:#"json"]];
for (NSDictionary * annotationDictionary in [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:NULL])
{
ADClusterableAnnotation * annotation = [[ADClusterableAnnotation alloc] initWithDictionary:annotationDictionary];
[annotations addObject:annotation];
[annotation release];
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Building KD-Tree…");
[self.mapView setAnnotations:annotations];
});
});
[annotations release];
}
so i saw that i have two ViewControllers which holds the the seedfile
this one is
#import "CDStreetlightsMapViewController.h"
#implementation CDStreetlightsMapViewController
- (NSString *)pictoName {
return #"CDStreetlight.png";
}
- (NSString *)clusterPictoName {
return #"CDStreetlightCluster.png";
}
- (NSString *)seedFileName {
return #"CDStreetlights";
}
- (NSString *)seedFileName1 {
return #"CDToilets";
}
#end
the other one is
#import "CDToiletsMapViewController.h"
#implementation CDToiletsMapViewController
- (NSString *)seedFileName {
return #"CDToilets";
}
- (NSString *)pictoName {
return #"CDToilet.png";
}
- (NSString *)clusterPictoName {
return #"CDToiletCluster.png";
}
#end
The json files are named CDToilets and CDStreetlights... but i have a tab bar which holds Toilets and streetlights. But lets say i would like to display on the toilets viewController the streetlights and the toilets? thats my problem right know.. I tried this
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray * annotations = [[NSMutableArray alloc] init];
self.mapView.visibleMapRect = MKMapRectMake(135888858.533591, 92250098.902419, 190858.927912, 145995.678292);
NSLog(#"Loading data…");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData * JSONData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:self.seedFileName ofType:#"json"]];
for (NSDictionary * annotationDictionary in [NSJSONSerialization JSONObjectWithData:JSONData options:kNilOptions error:NULL])
{
ADClusterableAnnotation * annotation = [[ADClusterableAnnotation alloc] initWithDictionary:annotationDictionary];
[annotations addObject:annotation];
[annotation release];
}
NSData * JSONData1 = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:self.seedFileName1 ofType:#"json"]];
for (NSDictionary * annotationDictionary in [NSJSONSerialization JSONObjectWithData:JSONData1 options:kNilOptions error:NULL])
{
ADClusterableAnnotation * annotation = [[ADClusterableAnnotation alloc] initWithDictionary:annotationDictionary];
[annotations addObject:annotation];
[annotation release];
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Building KD-Tree…");
[self.mapView setAnnotations:annotations];
});
});
[annotations release];
}
i renamed in the CDStreetlightsMapViewController the seedfile as seedfile1 so i can use it two times in viewDidLoad it didn't show errors but it didn't run well on simulator and I get an exception:
ClusterDemo[11014:c07]Loading data…
ClusterDemo[11014:1303]***Assertion failure in -[CDToiletsMapViewController seedFileName1], ADClusterMapView-master-7/ClusterDemo/Classes/CDMapViewController.m:86 2013-11-25 23:59:37.524
ClusterDemo[11014:1303]***Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This abstract method must be overridden!' *** First throw call stack: (0x18b2012 0x1277e7e 0x18b1e78 0xd0df35 0x3fad 0x36cb 0x2deb53f 0x2dfd014 0x2dee2e8 0x2dee450 0x99843e72 0x9982bdaa) libc++abi.dylib: terminate called throwing an exception (lldb)
you can download the full app here... https://github.com/applidium/ADClusterMapView
If you read the exception it's quite clear that you're using an abstract method that contains an assertion to ensure that you know you've made a mistake. You need to implement the seedFileName1 method in CDToiletsMapViewController (just like you did in CDStreetlightsMapViewController).
From the code I guess you need to add:
- (NSString *)seedFileName1 {
return #"CDStreetlights";
}
Related
I'm still new to using NSURL to get data and seem to have issues whenever trying to use this. In this case I use debug to check all the date coming in in ViewDidload and all the correct data comes in and is split into the arrays I then want to use to build my table view controller. However when we reach the NumberOfRows in section method, all of the arrays seem to have been reset to nil.
I've tried using various combinations of NSURL solutions but none seem to get any further than the one I am using right now (which at least shows some data arrriving). Can anyone please let me know if I am making an obvious mistake, or if not give me a reliable piece of code which I should use to perform a simple GET like this.
Thank you very much.
Here below my code:
#implementation MyLessonsTableViewController
NSArray *pastarr = nil;
NSArray *todoarr = nil;
NSArray *comingarr = nil;
NSArray *jsonless = nil;
- (void)viewDidLoad {
[super viewDidLoad];
// GET MY LESSONS FROM DATABASE
jsonless = [[NSArray alloc] init];
pastarr = [[NSArray alloc] init];
todoarr = [[NSArray alloc] init];
comingarr = [[NSArray alloc] init];
NSString *token = #"5cfd28bed3f5f5bd63143c81a50d434a";
NSString *urlString = [NSString stringWithFormat:#"http://soon.nextdoorteacher.com/apps/api/nextdoorteacher/student-lessons?t=%#", token];
NSURL *urlcc = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:urlcc];
NSError *error;
NSMutableDictionary *jsonLess = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
NSLog(#"My Lessons Json == %#", jsonLess);
// SPLIT ARRAY
NSArray *pastarr = [jsonLess valueForKeyPath:#"past"];
NSArray *todoarr = [jsonLess valueForKeyPath:#"todo"];
NSArray *comingarr = [jsonLess valueForKeyPath:#"upcoming"];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
NSUInteger lessonRowCount = 0;
switch (section) {
case 0:
lessonRowCount = todoarr.count;
break;
case 1:
lessonRowCount = comingarr.count;
break;
case 2:
lessonRowCount = pastarr.count;
break;
default:
break;
}
return lessonRowCount;
}
Several issues.
You call reloadData needlessly in dispatch_async.
You call reloadData before you process jsonLess.
You never assign anything to your array ivars.
You don't actually have ivars for your arrays. You have global variables.
Here's your posted code all fixed up:
#implementation MyLessonsTableViewController {
NSArray *pastarr = nil;
NSArray *todoarr = nil;
NSArray *comingarr = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
// GET MY LESSONS FROM DATABASE
NSString *token = #"5cfd28bed3f5f5bd63143c81a50d434a";
NSString *urlString = [NSString stringWithFormat:#"http://soon.nextdoorteacher.com/apps/api/nextdoorteacher/student-lessons?t=%#", token];
NSURL *urlcc = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:urlcc];
NSError *error;
NSMutableDictionary *jsonLess = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions
error:&error];
NSLog(#"My Lessons Json == %#", jsonLess);
// SPLIT ARRAY
pastarr = [jsonLess valueForKeyPath:#"past"];
todoarr = [jsonLess valueForKeyPath:#"todo"];
comingarr = [jsonLess valueForKeyPath:#"upcoming"];
[self.tableView reloadData];
}
Now this still suffers from one big problem. You are doing Internet access on the main thread. That's bad. You really should do it this way:
- (void)viewDidLoad {
[super viewDidLoad];
// GET MY LESSONS FROM DATABASE
NSString *token = #"5cfd28bed3f5f5bd63143c81a50d434a";
NSString *urlString = [NSString stringWithFormat:#"http://soon.nextdoorteacher.com/apps/api/nextdoorteacher/student-lessons?t=%#", token];
NSURL *urlcc = [NSURL URLWithString:urlString];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:urlcc];
NSError *error;
NSMutableDictionary *jsonLess = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions
error:&error];
NSLog(#"My Lessons Json == %#", jsonLess);
// SPLIT ARRAY
pastarr = [jsonLess valueForKeyPath:#"past"];
todoarr = [jsonLess valueForKeyPath:#"todo"];
comingarr = [jsonLess valueForKeyPath:#"upcoming"];
// Now this must be done on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}};
}
I’m building an article reading app.I’m using AMSliderMenu(https://github.com/SocialObjects-Software/AMSlideMenu)
library for menu list.When i click on any cell in AMSlideMenu it load into another table view which contain list of articles.
I’m fetching article data in uitableview with JSON.
The issue is when i click on list of menu in AMSlideMenu it takes time to open another view.How can i resolve this problem.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
BOOL myBool = [self isNetworkAvailable];
if (myBool)
{
#try {
_Title1 = [[NSMutableArray alloc] init];
_Author1 = [[NSMutableArray alloc] init];
_Images1 = [[NSMutableArray alloc] init];
NSData* data = [NSData dataWithContentsOfURL:ysURL];
NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if(ys_avatars){
for (int j=0;j<ys_avatars.count;j++)
{
[_Title1 addObject:ys_avatars[j][#"title"]];
[_Author1 addObject: ys_avatars[j][#"author"]];
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
}
}
else
{
NSLog(#"asd");
}
}
#catch (NSException *exception) {
}
[self LoadMore];
[self LoadMore];
});
});
}
}
-(void)LoadMore
{ BOOL myBool = [self isNetworkAvailable];
if (myBool)
{
#try {
// for table cell seperator line color
self.tableView.separatorColor = [UIColor colorWithRed:190/255.0 green:190/255.0 blue:190/255.0 alpha:1.0];
// for displaying the previous screen lable with back button in details view controller
UIBarButtonItem *backbutton1 = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStyleBordered target:nil action:nil];
[[self navigationItem] setBackBarButtonItem:backbutton1];
NSString *urlString=[NSString stringWithFormat:#"www.example.com&page=%d",x]; NSURL *newUrl=[NSURL URLWithString:urlString];
NSData* data = [NSData dataWithContentsOfURL:newUrl];
NSArray *ys_avatars = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
x++;
if(ys_avatars){
for (int j=0;j<_Title1.count ;j++)
{
[_Title1 addObject:ys_avatars[j][#"title"]];
[_Author1 addObject: ys_avatars[j][#"author"]];
[_Images1 addObject: ys_avatars[j][#"featured_img"]];
} }
else
{ NSLog(#"asd"); }
[self.tableView reloadData];}
#catch (NSException *exception) {
}} }
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _Title1.count;
}
Its because you have done many many works in viewDidLoad method that's executing synchronously, and that view controller will not be shown until the code in viewDidLoad will not executed completely.
So, load your data asynchronously or do it in viewDidAppear method, but I strongly suggest you to do it asynchronously.
I'm having trouble adding objects to an NSMutableArray. I clearly place 2 objects in the typeList array, but the count only shows up as 1. What am I doing wrong?
content.h
#interface TBContentModel : NSObject
+(NSMutableArray*)typeList;
+(void)setTypeList:(NSMutableArray*)str;
content.m
static NSMutableArray *typeList = nil;
#implementation TBContentModel
- (id) init {
self = [super init];
if (self) {
typeList = [NSMutableArray array];
}
return self;
}
contentviewcontroller.m
#implementation TBViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *jsonString = #"[{\"Content\":268,\"type\":\"text\"},{\"Content\":65,\"type\":\"number\"}]";
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *array = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
for (NSMutableDictionary *dictionary in array)
{
TBContentModel *test = [[TBContentModel alloc] init];
test.type = dictionary[#"type"];
[[TBContentModel typeList] addObject:test];
NSLog(#"%#", test.type);
}
}
- (IBAction)tapButton:(id)sender {
NSLog(#"%d", [TBContentModel.typeList count]); // always shows 1
}
You are re-creating your static typeList object everytime you allocate and initialize a new TBContentModel object.
Make the following changes:
static NSMutableArray *typeList = nil;
static dispatch_once_t once;
+ (NSMutableArray*)typeList {
dispatch_once(&once, ^{
typeList = [NSMutableArray array];
});
return typeList;
}
And remove the following line from your init method:
typeList = [NSMutableArray array];
Below is the method with the code, where I am getting a memory leak using manual memory management. The memory leak is detected by using Xcode instruments and specifically points to the line where I am using NSJSONSerialization. I am running the target app (on a device with iOS 6.1).
The first time that i tap on the refreshButton there is no leak. Any subsequent tap generates the leak(and more leaks on top of that if i continue tapping the button). Below is the code - This is basic stuff for consuming JSON web services(the web service link is bogus but the real one that I am using works). You will notice that I am using Grand Central Dispatch so that I can update the UI without waiting for the parsing of the JSON to finish.
The line detected by instruments is surrounded by the asterisks. I would like to get some help to anyone who might have an idea of what is going on here. The full stack trace(as mentioned in the below comments i will put here:)
+(NSJSONSerialization JSONObjectWithData:option:error:] -> -[_NSJSONReader parseData:options:] -> -[_NSJSONReader parseUTF8JSONData:skipBytes:options]->newJSONValue->newJSONString->[NSPlaceholderString
initWithBytes:length:encoding:]
-(void)parseDictionary:(NSDictionary *)dictonary{
self.transactions = [dictonary objectForKey:#"transactions"];
if(!self.transactions){
NSLog(#"Expected 'transactions' array");
return;
}
for (int arrayIndex = 0; arrayIndex < [self.transactions count]; arrayIndex++) {
TransactionResult *result = [[[TransactionResult alloc] init] autorelease];
result.transactionID = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"ID"];
result.transactionDescription = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"description"];
result.transactionPrice = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"price"];
self.totalPrice += [result.transactionPrice doubleValue];
NSLog(#"total price: %f", self.totalPrice);
[self.transactionResults addObject:result];
result = nil;
}
}
- (IBAction)refreshButtonPressed:(UIBarButtonItem *)sender {
__block id resultObject;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
NSURL *url = [NSURL URLWithString:#"http://mywebservice.php"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
***resultObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];***
if(!error){
if([resultObject isKindOfClass:[NSDictionary class]]){
NSDictionary *dictonary = resultObject;
[self parseDictionary:dictonary];
NSLog(#"Done parsing!");
dispatch_async(dispatch_get_main_queue(), ^{
self.isLoading = NO;
[self.transactionsTableView reloadData];
});
}
else{
NSLog(#"JSON Error: Expected Dictionary");
resultObject = nil;
return;
}
}
else{
NSLog(#"JSON Error: %#", [error localizedDescription]);
dispatch_async(dispatch_get_main_queue(), ^{
resultObject = nil;
[self.transactionsTableView reloadData];
[self showError];
});
return;
}
});
}
I used a ARC as soon as it came out with 4.3, and put an app in the store with it - point being you could switch to ARC. That said, I tried to reproduce your problem by creating a class/file that has the no-arc flag applied to it, but cannot reproduce the problem. This makes me believe your problem is elsewhere. In the code below, I create a Test object in another file, retain it, and send it the test message. No matter what I set "i" to, it always deallocs the object:
#import "Tester.h"
#interface Obj : NSObject <NSObject>
#end
#implementation Obj
- (id)retain
{
NSLog(#"retain");
id i = [super retain];
return i;
}
- (oneway void)release
{
NSLog(#"release");
[super release];
}
- (void)foo
{
}
- (void)dealloc
{
NSLog(#"Obj dealloced");
[super dealloc];
}
#end
#implementation Tester
- (void)test
{
int i = 2;
__block Obj *obj;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
obj = [[Obj new] autorelease];
if(i == 0) {
Obj *o = obj;
dispatch_async(dispatch_get_main_queue(), ^
{
[o foo];
} );
} else if(i == 1) {
obj = nil;
} else if(i == 2) {
dispatch_async(dispatch_get_main_queue(), ^
{
obj = nil;
} );
}
} );
}
#end
First of all, excuse me for my bad english but I'm french and I'll try my best to be understandable.
So, I'm coding a simple application with this structure :
- viewController class (deal with the UI)
- product class (define the object product)
- ws_product class (contains some functions which get json datas)
What I'm trying to do is to return the products array, that I get after I parsed my json in ws_product, in my viewController. Thanks to this I'll can fill my tableView and my application will no longer be empty !
My actual ws_product is :
#import "WS_Produit.h"
#import "Produit.h"
#import "ViewController.h"
#implementation WS_Produit
- (NSMutableArray *)getProduitsJSON
{
__block NSMutableArray *result;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
NSLog(#"on passe en async");
NSError *error = nil;
NSData *jsonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"the url to load"]];
NSDictionary *produits = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if( error )
{
NSLog(#"%#", [error localizedDescription]);
}
else {
dispatch_sync(dispatch_get_main_queue(), ^(){
NSLog(#"retour en sync");
result = [[NSMutableArray alloc] init];
Produit *tmp;
NSArray *produit = produits[#"produits"];
for ( NSDictionary *property in produit )
{
tmp = [Produit new];
tmp.ref = property[#"ref"];
tmp.name = property[#"name"];
tmp.description = property[#"description"];
tmp.price = property[#"price"];
tmp.imgURL = property[#"imgURL"];
[result addObject:tmp];
NSLog(#"%#", result);
}
});
}
});
NSLog(#"sortie du block");
NSLog(#"%#", result);
return result;
}
#end
My problem is when I'm out of the dispatch_queue my result array is empty so it's useless to return it in my viewController class, what can I do ?
Because you're using dispatch_async, your results array will be returned as empty before it gets filled.
Blocks are exactly what you need. They can be used as callbacks for async methods.
In your viewController, you should pass blocks to your method
[myObject getProduitsJSON:
success:^(NSArray *results){
// Use your results here
// Reload table for example.
}
failure:^(NSError *error){
// Use your error message (show it for example)
}];
So you're method should look like this:
-(void)getProduitsJson:(void(^)(NSArray* results))success failure:(void(^)(NSError* error))failure {
{
NSMutableArray *result = [[NSMutableArray alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() {
NSError *error = nil;
NSData *jsonData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"the url to load"]];
NSDictionary *produits = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if(error) {
failure(error);
}else{
// Fill your array
success(result);
}
}
}