Store annotation on map using state restoration - ios

I'm adding an annotation at the user position in my viewDidLoad and try store that annotation using state restoration.
I want the annotation to stay there even if the app gets terminated in the background.
-(void)viewDidLoad
{
...
self.annotationregion = MKCoordinateRegionMake(CLLocationCoordinate2DMake(self.mapview.userLocation.location.coordinate.latitude, self.mapview.userLocation.location.coordinate.longitude), MKCoordinateSpanMake(0.5, 0.5));
self.parkinglocation = [[SWAnnotation alloc] init];
self.parkinglocation.title = #"Here it is!";
self.parkinglocation.coordinate = self.annotationregion.center;
[self.mapview addAnnotation:self.parkinglocation];
NSLog(#"userlocation: %#", self.mapview.userLocation);
NSNumber *longitudeNumber = [NSNumber numberWithDouble:self.annotationregion.center.longitude];
NSNumber *latitudeNumber = [NSNumber numberWithDouble:self.annotationregion.center.latitude];
self.coordinateArray = #[latitudeNumber,longitudeNumber];
}
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
...
[coder encodeObject:self.coordinateArray forKey:#"CoordinateArray"];
}
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
...
NSArray *coordinateArray = [coder decodeObjectForKey:#"CoordinateArray"];
self.annotationregion = MKCoordinateRegionMake(CLLocationCoordinate2DMake([coordinateArray[0] doubleValue], [coordinateArray[1] doubleValue]), MKCoordinateSpanMake(1.5,1.5));
self.parkinglocation = [[SWAnnotation alloc] init];
self.parkinglocation.title = #"tata!";
self.parkinglocation.coordinate = self.annotationregion.center;
[self.mapview addAnnotation:self.parkinglocation];
}
Note, in the initial viewDidLoad annotation I use the title "Here it is!" and in the decodeRestorableStateWithCoder annotation I use the title "tata!" just to see which one it's showing me at the moment.
Now, this seems to work when the app gets terminated for the first time in the background. The app starts back up and has an annotation with the new title "tata!" at the right position.
The second time it gets terminated in the background and then gets started back up it shows me an annotation with the initial title of "Here it is!" off the coast of Africa, which of course is not my position...
Sooo I'm getting frustrated, what am I missing?

It seems to me that when you encode the state you actually encode self.coordinateArray, but when you restore you use a local variable NSArray *coordinateArray to restore.
So when your app will encode again later, your self.coordinateArray would be nil.
I think you should set [self setCoordinateArray: coordinateArray]; just after NSArray *coordinateArray = [coder decodeObjectForKey:#"CoordinateArray"];

Related

Current location not obtained in here maps

i am using here maps in my app and works fine. i pass destination coordinate from one view controller to another viewcontroller which contains here map and its methods. i tried to get the current location inside the method in the second view controller to calculate the roue, but current location always returns 0,0.please advice how to get current location inside the method where i pass the values from first viewcontroller
viewcontroller1:
- (IBAction)btn_navigate:(id)sender
{
NSLog(#"%#",_POiarray);
_VC=[[NaviMeVC alloc]init];
[_VC GetPoi:_POiarray];
[self.navigationController pushViewController:_VC animated:YES];
}
mapviewcontroller:
-(void)GetPoi:(NSMutableArray *)anArray
{
//get current location
NMAGeoPosition * position=[[NMAGeoPosition alloc]init];
position = [[NMAPositioningManager sharedPositioningManager] currentPosition];
_StartCoordinate=[[NMAGeoCoordinates alloc]initWithLatitude:position.coordinates.latitude longitude:position.coordinates.longitude];
//not working returns nil
[[NMAMapLoader sharedMapLoader] setDelegate:self];
_SelectedPOi=[[NSMutableArray alloc]init];
_SelectedPOi=anArray;
NSString *lat=[anArray valueForKey:#"latitude"];
CLLocationDegrees latitu=[lat doubleValue];
NSString *longi=[anArray valueForKey:#"longitude"];
CLLocationDegrees longit=[longi doubleValue];
_DestinationCoordinate=[[NMAGeoCoordinates alloc]initWithLatitude:latitu longitude:longit];
NSLog(#"%f %f",_DestinationCoordinate.latitude,_DestinationCoordinate.longitude);
NSNumber *lati = [NSNumber numberWithDouble:latitu];
NSNumber *longitu = [NSNumber numberWithDouble:longit];
NSDictionary *userLocation=#{#"lat":lati,#"long":longitu};
NMARoutingMode* routingMode = [[NMARoutingMode alloc] initWithRoutingType:NMARoutingTypeShortest
transportMode:NMATransportModeCar
routingOptions:0];
[self CalculateRoute:routingMode];
}
To start receiving positioning updates you need to call NMAPositioningManager startPositioning which I don't see in your sample code. More details in user guide link below. Please read the other instructions on that page to see if anything helps.
Another thing to check: have you added NSLocationWhenInUseUsageDescription into your projects Info.plist file to ensure your app can receive user location from CLLocationManager?
Also, this may seem obvious but please keep in mind that you will only be able to receive a valid value for currentPosition if you have a position fix.
Positioning User Guide
NMAPositioningManager Doxygen

Web call in DidUpdateLocations while app is terminated

As my user changes location, I need to compare his location with an array of locations I pull from the web. However, in my appDelegate, I'm not sure where exactly to place my code as I'm not sure what methods are called or not called when the app is terminated, but the CLLocationManager still works.
Specifically, I need to input this code where it will actually be called when the app is still terminated:
// alloc and init the various (Mutable)Array properties
self.locations = [[NSMutableArray alloc] init];
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[DealsModel alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
The _homeModel will then call this method:
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Set the downloaded items to the array
_locations = [items copy];
}
Which I will further edit to compare the user's location to the array of locations.
The thing is, this array of locations only changes once a week. Does the app really have to pull it from the web every time the user's location changes? Or is there a way to cache this and only pull it when self.locations has been deallocated?
This is what I have now, but I feel there must be a better way:
#interface AppDelegate () <CLLocationManagerDelegate>
{
DealsModel *_homeModel;
}
#property BOOL didRunBefore;
#property CLLocationManager *locationManager;
#property NSMutableArray *deals;
#end
#implementation AppDelegate
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Set the downloaded items to the array
_deals = [items copy];
[self compareSponsorLocations:_deals toUserLocation:[self.locationManager location]];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
if (!self.deals) {
// alloc and init the various (Mutable)Array properties
self.deals = [[NSMutableArray alloc] init];
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[DealsModel alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
} else {
[self compareSponsorLocations:self.deals toUserLocation:[locations lastObject]];
}
}
- (void) compareSponsorLocations: (NSArray *) array toUserLocation: (CLLocation *) location
{
for (Deal *deal in array) {
NSLog(#"%#", deal.name);
}
NSLog(#"%#", location.description);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
To handle location updates when you app is in background or even terminated, you can use "Significant Location Changes". It will trigger application to start in background mode when location has been changed significantly. Then you can start a background task to perform the operations on your need.
Documentation:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW8
Easy way to simulate it and debug:
XCode / iOS simulator: Trigger significant location change manually

How to make WSCoachMarksView walkthrough only run the first time user opens app?

I'm using WSCoachMarksView to give my users a walkthrough the first time they open up the app, and I obviously only want this to run that first time, and never again after that. I followed the instructions in the documentation that tells you to put the respective code that I have in viewDidAppearso that it only runs once, but it doesn't seem to work.
It still runs through the walkthrough every time the app is open. Is there anything I'm not doing properly to detect the app running previously and setting the BOOL so that it doesn't run again?
SearchViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
// Setup coach marks
NSArray *coachMarks = #[
#{
#"rect": [NSValue valueWithCGRect:(CGRect){{50,168},{220,45}}],
#"caption": #"Just browsing? We'll only notify you periodically of new matches. Need it soon? We'll notify you more frequently, and match you with items that are closer to you."
},
];
self.coachMarksView = [[WSCoachMarksView alloc] initWithFrame:self.tabBarController.view.bounds coachMarks:coachMarks];
[self.tabBarController.view addSubview:self.coachMarksView];
self.coachMarksView.animationDuration = 0.5f;
self.coachMarksView.enableContinueLabel = YES;
[self.coachMarksView start];
}
- (void)viewDidAppear:(BOOL)animated {
// Show coach marks
BOOL coachMarksShown = [[NSUserDefaults standardUserDefaults] boolForKey:#"WSCoachMarksShown"];
if (coachMarksShown == NO) {
// Don't show again
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"WSCoachMarksShown"];
[[NSUserDefaults standardUserDefaults] synchronize];
// Show coach marks
[self.coachMarksView start];
}
}
in viewDidLoad you always start your intro, regardless of what you've set in NSUserDefaults. The check in viewDidAppear is correct, but it's already playing at that time

Mapping a KML file to a Polyline in MKMapView

Overview:
I have a database of KML files that represent bus routes. I plan to plot a line (sometimes multiple lines if the route is complex) onto a MKMapView in iOS using Objective-C. However, I've been having a terribly difficult time with this, and need your help. You can view a few of the sample KML files I am using here:
Example A: A Full KML file that would be used in production
Example B: A Single Path from the above full file
Example C: A reformatted KML file as JSON
I have two methods of going at this. I have the JSON way and the XML way. Both don't work, with different results problems when using the different example KML files.
In both the JSON and the KML method, I use this code to return the overlay for the mapview
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay{
// Checking if the called overlay is a polyline
if([overlay class] == MKPolyline.class){
// Make the polyline
MKPolyline* polyline = (MKPolyline *)overlay;
#try {
// Extract the title and make sure it isnt nil
NSString *title = polyline.title;
if(title){
// Grab the PolyLine from the container object using the title
MKPolylineRenderer * rtn = [self.polylineContainer objectForKey:title];
if(rtn){
return rtn;
} else {
return nil;
}
}
}
#catch (NSException *exception) {
NSLog(#"%#",exception);
}
} else {
return nil;
}
}
The JSON Way
Personally I'd prefer to use the JSON way because Objective C does a better job with JSON serialization (in my poinion at least) than XML. I have a PHP script set up to just extract the <MultiGeometry> nodes as well as the <LineString> nodes. There doesn't appear to be an issue with this script, so I'll omit it from this question, but if you would like please ask and I'll add it.
The KML way would use example C above and always fails at the [self.mapView addOverlay:polyline]; line with an Unrecognized Selector sent to Instance. It also triggers an EXEC_BAD_ACCESS exception, but I can't trace to where it occurs (even with an exception breakpoint)
// A Synchrnous URLRequest is performed, and the JSON is serialised into response_root
// Metadata. Used for iteration later.
NSDictionary * meta = [response_root valueForKey:#"meta"];
NSInteger mg_count = [[meta valueForKey:#"MultiGeometryCount"] integerValue];
NSInteger ls_count = [[meta valueForKey:#"LineStringCount"] integerValue];
// The Data dictionary holds the data. Obviously
NSDictionary * data = [response_root valueForKey:#"data"];
// mgi is just short for MultiGeometry. It contains LineStrings (lsi)
int mgi = 0;
// Loop through the MultiGeometry nodes
while (mgi < mg_count) {
// Grab the Root Node
NSDictionary * root_node = [data valueForKey:[NSString stringWithFormat:#"root_%d",mgi]];
// lsi is just short for LineString. It contains the coordinates in a JSON object
int lsi = 0;
while (lsi < ls_count) {
// Grab the sub node containing all of the coordinate pairs
NSDictionary * sub_node = [root_node valueForKey:[NSString stringWithFormat:#"node_%d",lsi]];
NSInteger pair_count = [[sub_node valueForKey:#"CoordPairCount"] integerValue];
int pc = 0;
// Set up the C Array for the Coordinates
CLLocationCoordinate2D coordinates[pair_count];
// Loop through the pairs
while (pc < pair_count) {
// Grab the Pair Node
NSDictionary * pair_node = [sub_node valueForKey:[NSString stringWithFormat:#"set_%d",pc]];
// Set X and Y and add them to the coordinate array
double longtitude = [[pair_node valueForKey:#"x"] doubleValue];
double latitude = [[pair_node valueForKey:#"y"] doubleValue];
coordinates[pc].latitude = latitude;
coordinates[pc].longitude = longtitude;
pc ++;
}
// When we've finished with all of the pairs, we create the polyline
MKPolyline * polyline = [[MKPolyline alloc] init];
polyline = [MKPolyline polylineWithCoordinates:coordinates count:pair_count];
if(polyline){
#try {
// This always triggers a "Unrecognised selector sent to instance" exception. Although polyline is correctly set
[self.mapView addOverlay:polyline];
}
#catch (NSException *exception) {
NSLog(#"%#",exception);
}
// Create the rendered line and set its properties
MKPolylineRenderer * line = [[MKPolylineRenderer alloc] initWithPolyline:polyline];
line.strokeColor = [UIColor blueColor];
line.lineWidth = 2;
line.polyline.title = [NSString stringWithFormat:#"ls_%d", lsi];
// Add it to the polyline container, which is just a NSMutableDictionary
[self.polylineContainer setObject:line forKey:[NSString stringWithFormat:#"ls_%d", lsi]];
}
lsi ++;
}
mgi ++;
}
The KML Way
The KML way would use example A and B above and also always fails at the [self.mapView addOverlay:polyline]; line
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"coordinates"]) {
// The coordinate pairs are provided as one big string, I remove the garbage charters from it
coords = [coords stringByReplacingOccurrencesOfString:#"0 -" withString:#"-"];
coords = [coords stringByReplacingOccurrencesOfString:#"\n" withString:#""];
coords = [coords stringByReplacingOccurrencesOfString:#" " withString:#""];
coords = [coords stringByReplacingOccurrencesOfString:#"(null)" withString:#""];
// Split the string into a big array
NSArray * t = [[NSArray alloc] initWithArray:[coords componentsSeparatedByString:#","]];
// Create the C Array for the coordinates
CLLocationCoordinate2D coordinates[t.count];
// Because the X coordinate comes first, I use this toggle to go back between setting the X then the Y and loop
int isXorY = 0;
int i = 0;
double x = 0;
double y = 0;
for (NSString * c in t) {
// X always comes first
if(isXorY == 0){
isXorY = 1;
x = [c doubleValue];
// Then comes the Y
} else if(isXorY == 1){
isXorY = 0;
y = [c doubleValue];
}
// If both the X and the Y coordinate are set, add the pair to the Coordinates array and start over again
if(x != 0 && y != 0){
coordinates[i].latitude = y;
coordinates[i].longitude = x;
x = 0;
y = 0;
i ++;
}
}
// Create the Polyline using the coordinates
MKPolyline *polyline = [MKPolyline polylineWithCoordinates:coordinates count:i];
// This always triggers a "Unrecognised selector sent to instance" exception. Although polyline is correctly set
[self.mapView addOverlay:polyline];
// Create the polyline and set its properties
MKPolylineRenderer * line = [[MKPolylineRenderer alloc] initWithPolyline:polyline];
line.strokeColor = [UIColor blueColor];
line.lineWidth = 2;
line.polyline.title = [NSString stringWithFormat:#"ls_%d", totalCordPairs];
// Add it to the container object with its name. Polylinecontainer is just a NSMutableDictionary
[self.polylineContainer setObject:line forKey:[NSString stringWithFormat:#"ls_%d", totalCordPairs]];
// This is global integer that is used with the above name
totalCordPairs ++;
}
}
The results
When this actually does work (really rare) I get completely messed up results. It's easiest just to show you with pictures:
As you can see, the polylines loop back, and sometimes they just go across the map all the way to this village in France! That's where that stray red line is going in the first picture.
There are at least two separate problems causing the behavior you're seeing:
The rendererForOverlay delegate method is getting called before the polylineContainer has been updated with the required MKPolylineRenderer and the delegate method ends up returning nothing (not even nil).
You can't really assume when the map view will call its delegate methods but the rendererForOverlay is called when an overlay is in the visible region. This would also explain why it works "sometimes" which would be when you add the overlay when it's not in the visible region and the delegate method gets called after you've created and added the renderer.
In this case, the polyline's title will still be nil (because you're setting the polyline's title after calling addOverlay).
Since the current code in rendererForOverlay doesn't handle the case where title is nil and the method returns nothing in this scenario.
The method returning nothing (not even nil) is what causes the exception when addOverlay is called. Basically the map view ends up accessing garbage values for the renderer which in your case cause a "unrecognized selector" exception.
It's good practice to always return at least nil at the very end to handle unforeseen cases like this.
The real fix, however, is to move the creation of the renderer to the rendererForOverlay delegate method. You can still keep your polylineContainer approach but if the object is not found in there, then create and add the renderer right then and there in the delegate method.
Here's an example of the fix...
In the section where you create the MKPolyline:
//MKPolyline * polyline = [[MKPolyline alloc] init];
//The above alloc+init is unnecessary since the polylineWithCoordinates
//method effectively does that for you.
MKPolyline * polyline = [MKPolyline polylineWithCoordinates:coordinates
count:pair_count];
//set the polyline's title BEFORE adding it to the map view...
polyline.title = [NSString stringWithFormat:#"ls_%d", lsi];
//Call addOverlay but then do NOT create the renderer
//and add to polylineContainer HERE. Comment that code out.
Then in rendererForOverlay:
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay{
// Checking if the called overlay is a polyline
if([overlay class] == MKPolyline.class){
// Make the polyline
MKPolyline* polyline = (MKPolyline *)overlay;
#try {
// Extract the title and make sure it isnt nil
NSString *title = polyline.title;
if(title){
// Grab the PolyLine from the container object using the title
MKPolylineRenderer * rtn = [self.polylineContainer objectForKey:title];
//HERE, if we did not get already-created renderer,
//create it now and add to polylineContainer...
if (rtn == nil)
{
// Create the rendered line and set its properties
rtn = [[MKPolylineRenderer alloc] initWithPolyline:polyline];
rtn.strokeColor = [UIColor blueColor];
rtn.lineWidth = 2;
// Add it to the polyline container, which is just a NSMutableDictionary
[self.polylineContainer setObject:rtn forKey:title];
}
if(rtn){
return rtn;
} else {
return nil;
}
}
}
#catch (NSException *exception) {
NSLog(#"%#",exception);
}
} else {
return nil;
}
//Always return a default from a method
//that is supposed to return something...
return nil;
}
By the way, it's not clear why you are storing the renderers in polylineContainer. If you're thinking that creating the renderers is expensive and you want to optimize performance, ok but that might be premature at this point (unless you've already found this to be required in your case).
The cause of the lines "looping back" or appearing in unexpected locations like a village in France might be due to just bad data. Look at or log the coordinates you are adding to the polyline and confirm that they are correct. For example, in the JSON file you linked to in the question, there are lots of these:
"set_19":{"y":"0"}
This would end up adding a coordinate at 0,0 which is in the Atlantic Ocean off the west coast of Africa. It seems that either the data or how the code is interpreting the data is wrong.

Variables and Transferring Data between View Controllers

I know that there are tutorials everywhere, but I can't figure this out for some reason. I have a tab bar controller. Each tab links to a navigation controller, which is segued to a view controller. So, 2 main view controllers (StatusVC and TransactionsVC).
In StatusVC, I have a text field. In TransVC, I have a table view. A person adds a cell to the table. Math is done behind the scenes. The cell values are added together (numbers). This information is sent back to StatVC for calculations and displaying of the data. I've already got the math part down. My question: how do I transfer the data between view controllers, and better yet, how do I store this data so that it doesn't get deleted on quit (NSUserDefaults probably)?
This can be broken down I suppose, the transferring of data, the saving of data, and the displaying of data when the tab is pressed and view is shown.
I'm hoping this is making sense. Anyway, here's the code I've got. You're looking at TranVC. User enters data into the table with an alert view. You are looking at part of the Alert View delegate methods. This is when the user enters data into a cell (presses done). Look for key areas with the ******* comments.
StatusViewController *statVC = [[StatusViewController alloc]init]; //*******init
// Set the amount left in the budget
NSString *amountToSpend = statVC.amountLeftInBudget.text;
double budgetLabel = [amountToSpend doubleValue];
NSString *lastItem = [transactions objectAtIndex:0];
double lastLabel = [lastItem doubleValue];
double totalValue = budgetLabel - lastLabel;
NSString *amountToSpendTotal = [NSString stringWithFormat: #"%.2f", totalValue];
statVC.amountLeftInBudget.text = amountToSpendTotal; //*******set text (but not save), either way, this doesn't work
// Set the amount spent
NSString *sum = [transactions valueForKeyPath:#"#sum.self"];
double sumLabel = [sum doubleValue];
NSString *finalSum = [NSString stringWithFormat:#"%.2f", sumLabel];
//Set the amountSpent label
statVC.amountSpent.text = finalSum; //*******set text (but not save), either way, this doesn't work
// The maxed out budget section
if ([statVC.amountLeftInBudget.text isEqualToString: #"0.00"]) //*******set color (but not save), either way, this doesn't work
{
statVC.amountLeftInBudget.textColor = statVC.currencyLabel.textColor = [UIColor redColor];
} else if ([statVC.amountLeftInBudget.text compare:#"0.00"] == NSOrderedAscending)
{
statVC.amountLeftInBudget.textColor = statVC.currencyLabel.textColor = [UIColor redColor];
} else if ([statVC.amountLeftInBudget.text compare:#"0.00"] == NSOrderedDescending)
{
statVC.amountLeftInBudget.textColor = statVC.currencyLabel.textColor = [UIColor colorWithRed:23.0/255.0 green:143.0/255.0 blue:9.0/255.0 alpha:1.0];
}
if ([statVC.amountLeftInBudget.text compare:#"0.00"] == NSOrderedAscending)
{
// Create our Installation query
UIAlertView *exceed;
exceed = [[UIAlertView alloc]
initWithTitle: #"Budget Exceeded"
message: #"You have exceeded your budget amount"
delegate: self
cancelButtonTitle: #"Okay"
otherButtonTitles: nil];
[exceed show];
}
Any help with this would be amazing.
This is indeed a common question.
There are various solutions. The one I recommend is to use a data container singleton. Do a google search on the singleton design pattern in Objective C. You'll even find examples of it here on SO.
Create a singleton with properties for the values that you want to share. Then teach your singleton to save it's data. You can use user defaults, you can use NSCoding, you can extract the data to a dictionary and save it to a plist file in your documents directory, or various other schemes as well.
Like Duncan suggested, a Singleton pattern might be the best route to go. If you place the shared data into a model class, you can create a class method that can be used to acquire a singleton object.
MyModel.m
#implementation MyObject
- (id) init
{
return nil; // We force the use of a singleton. Probably bad practice?
}
// Private initializer used by the singleton; not included in the header file.
- (id)initAsSingleton {
self = [super init];
if (self) {
// Initialize your singleton instance here.
}
return self;
}
+ (MyModel *)sharedMyModel {
static MyModel *myModel = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
myModel = [[MyModel alloc] initAsSingleton];
});
return myModel;
}
MyModel.h
#interface MyModel : NSObject
+ (MyModel *)sharedMyModel; // Singleton instance.
#end
This does not protect against you using [[MyModel alloc] init];. It returns a nil object which is probably poor programming on my end, but it does force you to use the singleton object instead. To use in each one of your view controllers, you just use the following line to grab the singleton instance.
MyModel *model = [MyModel sharedMyModel];
Store the data into it, and return to your other view controller and grab the singleton again. You'll have all of your data.
After thinking about it, you could also force the default initializer to just return your singleton instance like:
- (id)init {
return [MyModel sharedMyModel];
}

Resources