NSMutableDictionary setObject:forKey: fails to add key - nsmutabledictionary

I'm sure I'm missing something in a small iPhone program I'm trying to write, but the code is simple and it compiles without any errors and so I fails to see where the error is.
I've set up a NSMutableDictionary to store students' attributes, each with a unique key. In the header file, I declare the NSMutableDictonary studentStore:
#interface School : NSObject
{
#private
NSMutableDictionary* studentStore;
}
#property (nonatomic, retain) NSMutableDictionary *studentStore;
And of course in the implementation file:
#implementation School
#synthesize studentStore;
And I want to add an object into the dictionary:
- (BOOL)addStudent:(Student *)newStudent
{
NSLog(#"adding new student");
[studentStore setObject:newStudent forKey:newStudent.adminNo];
return YES;
}
class Student has the attributes:
#interface Student : NSObject
{
#private
NSString* name; //attributes
NSString* gender;
int age;
NSString* adminNo;
}
where newStudent has the values:
Student *newStudent = [[Student alloc] initWithName:#"jane" gender:#"female" age:16 adminNo:#"123"];
But when I look up the dictionary:
- (void)printStudents
{
Student *student;
for (NSString* key in studentStore)
{
student = [studentStore objectForKey:key];
NSLog(#" Admin No: %#", student.adminNo);
NSLog(#" Name: %#", student.name);
NSLog(#"Gender: %#", student.gender);
}
NSLog(#"printStudents failed");
}
It fails to print the values in the table. Instead, it prints the line "printStudents failed".
I guess this's quite basic, but since I'm new to iOS programming I'm a bit stumped. Any help will be appreciated. Thanks.

Your studentStore instance variable is a pointer to an NSMutableDictionary. By default, it points to nil, meaning it doesn't point to any object. You need to set it to point to an instance of NSMutableDictionary.
- (BOOL)addStudent:(Student *)newStudent
{
NSLog(#"adding new student");
if (studentStore == nil) {
studentStore = [[NSMutableDictionary alloc] init];
}
[studentStore setObject:newStudent forKey:newStudent.adminNo];
return YES;
}

Related

How to use delegate here? I can't get it to work

I know there is very simple question, but few hours I can not do this.
I got 2 classes:
1. LoginViewController : UIViewController
2. WebRequests : NSObject - it makes request and got response from server (singleton)
Then I got response I want to run MyMethod in LoginViewController from WebRequests.
I do it like here, but it not works:
WebRequests.h
#protocol WebRequestsDelagate <NSObject>
#required
- (void) MyMethod;
#end
#interface WebRequests : NSObject
...
#property (nonatomic, weak) id <WebRequestsDelagate> delegate;
WebRequests.m
#implementation WebRequests
//singleton
+ (WebRequests *)sharedInstance {
static dispatch_once_t p = 0;
__strong static id _sharedObject = nil;
dispatch_once(&p, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
- (id)init {
if (self = [super init]) {
LoginViewController * loginViewController = [[LoginViewController alloc] init];
self.delegate = loginViewController;
}
return self;
}
- (void) someMethod {
//here I got response
[self.delegate runThisMethod];
I do not move on in runThisMethod in LoginViewController.
Why does it not work?
UPDATE
#implementation LoginViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
return YES;
}
If that's your real code, then the loginViewController created in the [WebRequests init] method will be destroyed at the end of the if statement (i.e. pretty much straight away):
- (id)init {
if (self = [super init]) {
LoginViewController * loginViewController = [[LoginViewController alloc] init];
self.delegate = loginViewController;
}
return self;
}
I don't understand why you are creating view controllers in this method; it would be more normal to create the view controller outside of this class (through whatever method) and then register it as the delegate of the WebRequests object.
Okay, I'm a bit confused by your code but maybe you're looking for something like
WebRequests *webRequests = [WebRequests sharedInstance];
webRequests.delegate = self;
perhaps in LoginController's viewDidLoad.

Calling coordinates from MySQL database with this code causes simulator to display a SIGABRT error. What's wrong?

When I use this code to try and call Lat & Long coordinates from a MySQL database (and display them on a MapView), the app runs fine. Everything appears to work, but for some reason, xcode pauses the simulator before the map loads, and throws me a SIGABRT. Any idea why? See below:
MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface MapViewController : UIViewController <MKMapViewDelegate>
#property (nonatomic, strong) IBOutlet MKMapView *mapView;
#property (nonatomic, retain) NSMutableArray *dispensaries;
#property (nonatomic, retain) NSMutableData *data;
#end
MapViewController.m
#import "MapViewController.h"
#import "MapViewAnnotation.h"
#implementation MapViewController
#synthesize mapView;
#synthesize dispensaries;
#synthesize data;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad {
[super viewDidUnload];
NSLog(#"Getting Device Locations");
NSString *hostStr = #"http://stylerepublicmagazine.com/dispensaries.php";
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:hostStr]];
NSString *serverOutput = [[NSString alloc] initWithData:dataURL encoding: NSASCIIStringEncoding];
NSLog(#"server output: %#", serverOutput);
NSMutableArray *array = (NSMutableArray *)dispensaries;
dispensaries = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
for (NSDictionary *dictionary in array) {
CLLocationCoordinate2D coord = {[[dictionary objectForKey:#"lat"] doubleValue], [[dictionary objectForKey:#"lng"] doubleValue]};
MapViewAnnotation *ann = [[MapViewAnnotation alloc] init];
ann.title = [dictionary objectForKey:#"Name"];
ann.coordinate = coord;
[mapView addAnnotation:ann];
[mapView setMapType:MKMapTypeStandard];
[mapView setZoomEnabled:YES];
[mapView setScrollEnabled:YES];
self.mapView.delegate = self;
}
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 800, 800);
[self.mapView setRegion:[self.mapView regionThatFits:region] animated:YES];
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = userLocation.coordinate;
point.title = #"You Are Here";
point.subtitle = #"Your current location";
[self.mapView addAnnotation:point];
}
Ok, so now you have the JSON. You are going to have to decode that using NSXMLParser. Or, if you want to turn that into objects, you could use my open source library on GitHub, which allows you to simply implement NSCoding protocol and instantiate objects (or encode them) as JSON. That's here.

Getting the value of a delegate upon clicking back?

I have two class files. ViewController and ChooseServerView. A user clicks a cell within the ViewController class which pushes to the ChooseServerView. When a user makes a selection in ChooseServerView the value of that cell gets passed to the delegate. Now, these two views are in front of a navigation controller, so in the ChooseServerView there is a back button. When the user clicks back I want to update the cell in the first view with the new value from the delegate, make sense?
ViewController.h
#import <UIKit/UIKit.h>
#import "ChooseServerView.h"
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, serverUserChoice>
{
NSString *testLocation;
}
#property (nonatomic, retain) NSString *testLocation;
#end
View Controller.m
#import "ViewController.h"
#import "ChooseServerView.h"
#import "AppDelegate.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize testLocation;
- (void)viewDidLoad
{
NSUserDefaults *sharedPref = [NSUserDefaults standardUserDefaults];
testLocation =[sharedPref stringForKey:#"defaultLocation"];
NSLog(#"Location Chosen: %#",testLocation);
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark - Table View Methods
- (void) userDidChoose:(NSString *) server {
testLocation = server;
NSLog(#"Test Location %#", server);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // Default is 1 if not implemented
{
return 2;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; // fixed font style. use custom view (UILabel) if you want something different
{
switch (section) {
case 0:
return #"Choose Test Location:";
break;
case 1:
return #"Choose Test Type:";
default:
return #"Unknown";
break;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
switch (section) {
case 0:
return 1;
break;
case 1:
return 1;
default:
return 0;
break;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
UITableViewCell *serverLocCell = [tableView dequeueReusableCellWithIdentifier:#"serverLocation"];
switch (indexPath.section) {
case 0:
serverLocCell.textLabel.text = testLocation;
serverLocCell.detailTextLabel.text = #"Change";
break;
case 1:
serverLocCell.textLabel.text = #"Speed Test";
serverLocCell.detailTextLabel.text = #"Change";
break;
default:
break;
}
return serverLocCell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section) {
case 0:
[self performSegueWithIdentifier:#"toServerChoice" sender:self];
break;
case 1:
[self performSegueWithIdentifier:#"toTestType" sender:self];
break;
default:
break;
}
}
#end
ChooseServerView.h
#import <UIKit/UIKit.h>
#class ViewController;
#protocol serverUserChoice <NSObject>
#optional
- (void)userDidChoose:(NSString *) server;
#end
#interface ChooseServerView : UIViewController <UITableViewDataSource, UITableViewDelegate, serverUserChoice>
{
NSArray *serverSelection;
NSArray *tqServerSelection;
}
#property (nonatomic, retain) NSArray *serverSelection;
#property (nonatomic, retain) NSArray *qServerSelection;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property(retain) NSIndexPath* lastIndexPath;
#property (nonatomic, assign) id <serverUserChoice> serverDelegate;
#end
ChooseServerView.m
#import "ChooseServerView.h"
#import "ViewController.h"
#import "AppDelegate.h"
#define totalSections 2
#define standardSection 0
#define qualitySection 1
#interface ChooseServerView ()
#end
#implementation ChooseServerView;
#synthesize serverSelection;
#synthesize qServerSelection;
#synthesize lastIndexPath;
#synthesize serverDelegate;
- (void)viewDidLoad
{
serverSelection = [[NSArray alloc] initWithObjects:#"Chicgo, IL",#"London, UK",#"San Jose, CA",#"Washington, DC", nil];
qServerSelection = [[NSArray alloc] initWithObjects:#"Chicgo, IL (Q)",#"London, UK (Q)",#"San Jose, CA (Q)",#"Washington, DC (Q)", nil];
[super viewDidLoad];
}
#pragma mark - Table View Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // Default is 1 if not implemented
{
return totalSections;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; // fixed font style. use custom view (UILabel) if you want something different
{
switch (section) {
case standardSection:
NSLog(#"Std Heading Set");
return #"Standard Test Locations:";
break;
case qualitySection:
NSLog(#"Qual Heading Set");
return #"Quality Test Locations:";
break;
default:
NSLog(#"Section Count Error");
return #"Section Count Error";
break;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
{
if (section == 0) {
NSLog(#"Std Loc Set");
return [serverSelection count];
}
else {
NSLog(#"Quality Loc Set");
return [qServerSelection count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
UITableViewCell *serverLoc = [tableView dequeueReusableCellWithIdentifier:#"serverSelection"];
switch (indexPath.section) {
case standardSection:
serverLoc.textLabel.text = [self.serverSelection objectAtIndex:indexPath.row];
break;
case qualitySection:
serverLoc.textLabel.text = [self.qServerSelection objectAtIndex:indexPath.row];
break;
default:
break;
}
return serverLoc;
}
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellvalue;
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
cellvalue = cell.textLabel.text;
[serverDelegate userDidChoose:cellvalue];
NSLog(#"Cell Selected is %#",cellvalue);
[cell setSelected:FALSE animated:TRUE];
}
#end
I can see the correct value gets passed to the delegate, I just don't know how to "call" that delegate/method when going back using the automatic back button implemented by the nav controller.
Any ideas would be great.
As per answer below:
Added to my ViewController.h
#property (nonatomic, weak) IBOutlet UITableView *tableView;
Added to my ViewController.m
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView reloadData];
}
Still no joy at this stage. First cell fails to update upon pressing the back button as provided by the navigation controller.
Looks like you are almost there.
I think the issue is that your UITableView is not being reloaded to represent the updated value of testLocation. I don't see a reference to the UITableView that is being managed by your ViewController, first add an outlet to ViewController.m to point to this table view.
Then, add the following in your ViewController.m
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView reloadData];
}
You also need to make sure you set your ViewController as the delegate of ChooseServerViewController. (You should rename ChooseServerView.m to ChooseServerViewController.m for clarity, as it's a view controller subclass- I've referred to it as such below)
You can set the delegate by adding a prepare for segue method in ViewController.m - something like:
(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
ChooseServerViewController *viewController = (ChooseServerViewController *)segue.destinationViewController;
viewController.serverDelegate = self;
}
I would also look into using UITableViewController for these two controller classes, since it appears that's the type of functionality you are looking for.

UILabel to control a UItextfield in another view controller

I am using Xcode 4.3 and I have a label in one view controller that I want to update the text in a text field in another view controller. How should I do this?
You should put the text in your model class (assuming that you have one; if you don't, you need to create it). When the end-user edits the text field, your code should change the string in your model; when the label displays, you should read its text from the model. The easiest way of sharing your model among multiple classes is to define a singleton.
Header:
#interface Model : NSObject
#property (NSString*) labelText;
+(Model*)instance;
#end
Implementation:
#implementation Model
#synthesize labelText;
+(Model*)instance{
static Model *inst;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
inst = [[Model alloc] init];
});
return inst;
}
-(id)init {
if (self = [super init]) {
labelText = #"Initial Text";
}
return self;
}
#end
Using the model:
// Setting the field in the model, presumably in textFieldDidEndEditing:
// of your UITextField delegate
[Model instance].labelText = textField.text;
// Getting the field from the model, presumably in viewWillAppear
myLabel.text = [Model instance].labelText;

Mapping NSManagedObject Entity to custom class with RestKit

I am able to pull JSON results and map them to the db. However, as soon as I try and map the entity to my subclass, it seems that my mapping breaks.
JSON Sample:
{"Customers":[{"Customer":{"Active":"Y","BranchID":0,"CustomerID":"SMCJIMB00736253","CustomerIdentifier":"","CustomerTypeID":0,"Name":""}}]}
Setup:
// Initialize RestKit
RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:TEST_SERVICES_ROOT];
// Initialize object store
objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:RKDefaultSeedDatabaseFileName];
Mapping:
RKManagedObjectMapping* customerMapping = [RKManagedObjectMapping mappingForClass:[Customer class]];
customerMapping.primaryKeyAttribute = #"customerID";
[customerMapping mapKeyPathsToAttributes:#"Active", #"active", nil];
[customerMapping mapKeyPathsToAttributes:#"BranchID", #"branchID", nil];
[customerMapping mapKeyPathsToAttributes:#"CustomerID", #"customerID", nil];
[customerMapping mapKeyPathsToAttributes:#"CustomerIdentifier", #"customerIdentifier", nil];
[customerMapping mapKeyPathsToAttributes:#"CustomerTypeID", #"customerTypeID", nil];
[customerMapping mapKeyPathsToAttributes:#"Name", #"name", nil];
Add Mapping:
[objectManager.mappingProvider setMapping:customerMapping forKeyPath:#"Customers.Customer"];
Call WS:
// Load the object model via RestKit
RKObjectManager* objectManager = [RKObjectManager sharedManager];
NSString *url = [NSString stringWithFormat:SERVICE_CUSTOMERS,#"0",#"1",#"100"];
[objectManager loadObjectsAtResourcePath:url delegate:self block:^(RKObjectLoader* loader) {
loader.objectMapping = [objectManager.mappingProvider objectMappingForClass:[Customer class]];
}];
#interface Customer : NSManagedObject
#property (nonatomic, retain) NSString* active;
#property (nonatomic, retain) NSNumber* branchID;
#property (nonatomic, retain) NSString* customerID;
#property (nonatomic, retain) NSString* customerIdentifier;
#property (nonatomic, retain) NSNumber* customerTypeID;
#property (nonatomic, retain) NSString* name;
//populated via fetch predicate
#property (nonatomic, retain) NSArray* customerAddress;
#end
#implementation Customer
#dynamic active;
#dynamic branchID;
#dynamic customerID;
#dynamic customerIdentifier;
#dynamic customerTypeID;
#dynamic name;
//populated via fetch predicate
#dynamic customerAddress;
#end
This works fine as long as I don't try and map the Customer class to the Entity in the data model. Ultimately, I am trying to use my Customer class as a MKAnnotation, but I can't since I cant provide a concrete implementation class. I'm sure that there is some sort of misunderstanding of RestKit to blame. I see that the RKTwitterCoreData has the RKStatus as a concrete subclass.
#end
Ok,
I figured it out after much trial and error! The issue was that I needed to set my rootKeyPath on my mapping to match the rootkeyPath that I use when I added the mapping to the mapping provider. The RKTwitterCoreData client does not do this, but I needed to for whatever reason.
RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class]];
userMapping.rootKeyPath = #"User";
userMapping.primaryKeyAttribute = #"userName";
[userMapping mapKeyPathsToAttributes:#"Authenticated", #"authenticated", nil];
................
[objectManager.mappingProvider setMapping:userMapping forKeyPath:#"User"];