Search code examples
iosobjective-cnsurlconnection

downloading entries from mysql database over multiple viewcontrollers


I have a very annoying issue with code I am working on in xcode for iOS, and it is driving me crazy!

This will take a lot of code explanation, as I need to include a lot of code.

I had a viewcontroller working, connecting to an online mysql database, and populating a tableview. This was all working great.

I then had to duplicate this code, as I want to download from another database and populate a mapview using co-ordinates from another mysql database.

Screen5ViewController.h / Screen5ViewController.m are the files for the viewcontroller with the tableview.

ShopTable.h / ShopTable.m is the code that handles the downloading of the data from the database.

This was all working.

Screen2ViewController.h / Screen2ViewController.m / MapTable.h / MapTable.m are the files I duplicated to download from the other database to populate the mapview.

This doesnt work at all, and doesnt even call the method ("NSLog(@"map table called...");") - so it doesnt even try to download the data.

I dont understand why this method isnt even called?

The code compiles and runs with no errors and no warnings.

Any help anyone can give would be very much appreciated. I have spent so much time looking over this code and trying everything I can think of!

Screen5ViewController.h

#import <UIKit/UIKit.h>
#import "ShopTable.h"

@interface Screen5ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, ShopTableProtocol> {
}

@property (weak, nonatomic) IBOutlet UITableView *shopTableView;

@end

Screen5ViewController.m

#import "Screen5ViewController.h"

@interface Screen5ViewController () {
ShopTable *_shopTable;
NSArray *_feedItems;
}
@end

@implementation Screen5ViewController

@synthesize

- (void)viewDidLoad {
[super viewDidLoad];

self.title = @"Screen 5";

// Set this view controller object as the delegate and data source for the table view
self.shopTableView.delegate = self;
self.shopTableView.dataSource = self;

// Create array object and assign it to _feedItems variable
_feedItems = [[NSArray alloc] init];

// Create new ShopTable object and assign it to _shopTable variable
_shopTable = [[ShopTable alloc] init];

// Set this view controller object as the delegate for the home model object
_shopTable.delegate = self;

// Call the download items method of the home model object
[_shopTable downloadItems2];
}

-(void)itemsDownloaded2:(NSArray *)items {
// This delegate method will get called when the items are finished downloading

// Set the downloaded items to the array
_feedItems = items;

// Reload the table view
[self.shopTableView reloadData];
}

#pragma mark Table View Delegate Methods

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of feed items (initially 0)
return _feedItems.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Retrieve cell
NSString *cellIdentifier = @"shopTableCell";
UITableViewCell *myCell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

// Get the location to be shown
Location2 *item = _feedItems[indexPath.row];

// Get references to labels of cell

myCell.textLabel.text = item.name;
myCell.detailTextLabel.text = item.address;

return myCell;
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}

@end

ShopTable.h

#import <Foundation/Foundation.h>

@protocol ShopTableProtocol <NSObject>
- (void)itemsDownloaded2:(NSArray *)items;
@end

@interface ShopTable : NSObject <NSURLConnectionDataDelegate>
@property (nonatomic, weak) id<ShopTableProtocol> delegate;
- (void)downloadItems2;
@end

@interface Location2 : NSObject
@property (nonatomic, strong) NSString *latitude;
@property (nonatomic, strong) NSString *longitude;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *address;
@end

ShopTable.m

#import "ShopTable.h"

@interface ShopTable() {
NSMutableData *_downloadedData;
}
@end

@implementation ShopTable

- (void)downloadItems2 {
NSLog(@"shop table called...");

// Download the json file
NSURL *jsonFileUrl = [NSURL URLWithString:@"http://www.example.com/shop.php"];

// Create the request
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:jsonFileUrl];

// Create the NSURLConnection
[NSURLConnection connectionWithRequest:urlRequest delegate:self];
}

#pragma mark NSURLConnectionDataProtocol Methods

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// Initialize the data object
_downloadedData = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the newly downloaded data
[_downloadedData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Create an array to store the locations
NSMutableArray *_locations = [[NSMutableArray alloc] init];

// Parse the JSON that came in
NSError *error;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:_downloadedData options:NSJSONReadingAllowFragments error:&error];

// Loop through Json objects, create question objects and add them to our questions array
for (int i = 0; i < jsonArray.count; i++) {
    NSDictionary *jsonElement = jsonArray[i];

    // Create a new location object and set its props to JsonElement properties
    Location2 *newLocation = [[Location2 alloc] init];
    newLocation.name = jsonElement[@"title"];
    newLocation.address = jsonElement[@"description"];
    newLocation.latitude = jsonElement[@"Latitude"];
    newLocation.longitude = jsonElement[@"Longitude"];

    // Add this question to the locations array
    [_locations addObject:newLocation];
}

// Ready to notify delegate that data is ready and pass back items
if (self.delegate) {
    [self.delegate itemsDownloaded2:_locations];
}
}

@end

@implementation Location2
@end

Screen2ViewController.h

#import <UIKit/UIKit.h>
#import "MapTable.h"

@interface Screen2ViewController : UIViewController <MapTableProtocol> {
}

@end

Screen2ViewController.m

#import "Screen2ViewController.h"

@interface Screen2ViewController () {
MapTable *_mapTable;
NSArray *_feedItems1;
}
@end

@implementation Screen2ViewController

@synthesize

- (void)viewDidLoad {
[super viewDidLoad];

self.title = @"Screen 2";

NSLog(@"1"); // This is called fine!

// Create array object and assign it to _feedItems variable
_feedItems1 = [[NSArray alloc] init];

NSLog(@"2"); // This is called fine!

// Set this view controller object as the delegate for the home model object
_mapTable.delegate = self;

NSLog(@"3"); // This is called fine!

// Call the download items method of the home model object
[_mapTable downloadItems];
}

NSLog(@"4"); // This is called fine!

-(void)itemsDownloaded:(NSArray *)items {
// This delegate method will get called when the items are finished downloading

// Set the downloaded items to the array
_feedItems1 = items;

NSLog(@"%lu locations downloaded ok!", (unsigned long)_feedItems1.count);
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}

@end

MapTable.h

#import <Foundation/Foundation.h>

@protocol MapTableProtocol <NSObject>
- (void)itemsDownloaded:(NSArray *)items;
@end

@interface MapTable : NSObject <NSURLConnectionDataDelegate>
@property (nonatomic, weak) id<MapTableProtocol> delegate;
- (void)downloadItems;
@end

@interface Location : NSObject
@property (nonatomic, strong) NSString *latitude;
@property (nonatomic, strong) NSString *longitude;
@property (nonatomic, strong) NSString *name;
@end

MapTable.m

#import "MapTable.h"

@interface MapTable() {
NSMutableData *_downloadedData1;
}
@end

@implementation MapTable

- (void)downloadItems {
NSLog(@"map table called..."); // THIS CODE NEVER GETS CALLED??!!

// Download the json file
NSURL *jsonFileUrl = [NSURL URLWithString:@"http://www.example.com/map.php"];

// Create the request
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:jsonFileUrl];

// Create the NSURLConnection
[NSURLConnection connectionWithRequest:urlRequest delegate:self];
}

#pragma mark NSURLConnectionDataProtocol Methods

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// Initialize the data object
_downloadedData1 = [[NSMutableData alloc] init];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the newly downloaded data
[_downloadedData1 appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Create an array to store the locations
NSMutableArray *_locations = [[NSMutableArray alloc] init];

// Parse the JSON that came in
NSError *error;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:_downloadedData1 options:NSJSONReadingAllowFragments error:&error];

// Loop through Json objects, create question objects and add them to our questions array
for (int i = 0; i < jsonArray.count; i++) {
    NSDictionary *jsonElement = jsonArray[i];

    // Create a new location object and set its props to JsonElement properties
    Location *newLocation = [[Location alloc] init];
    newLocation.name = jsonElement[@"firstname"];
    newLocation.latitude = jsonElement[@"mylatitude"];
    newLocation.longitude = jsonElement[@"mylongitude"];

    // Add this question to the locations array
    [_locations addObject:newLocation];
}

// Ready to notify delegate that data is ready and pass back items
if (self.delegate) {
    [self.delegate itemsDownloaded:_locations];
}
}

@end

@implementation Location
@end

Solution

  • In your Screen2ViewController.h:

    @interface Screen2ViewController : UIViewController <MKMapViewDelegate, MapTableProtocol> {
        IBOutlet MKMapView *mapView;
    }
    
    @property (nonatomic, retain) IBOutlet MKMapView *mapView;
    
    @end
    

    Your MKMapView from interface builder is probably linked to the @property, but everywhere in the Screen2ViewController.m you are working with mapView (instance variable which is never initialised) instead of self.mapView.


    Also, compared to Screen5ViewController.m you are missing MapTable initialisation in Screen2ViewController's -viewDidLoad method:

    _mapTable = [[MapTable alloc] init];