Map coordinates not decoding - objective-c

In my app, I am trying to save the pins that are on the map so that they are there when the user opens the app after it is terminated. I have conformed my mkAnnotation class to NSCoding, and implemented the two required methods. The annotations are all stored in a NSMutableArray in a singleton class, so I am really just trying to save the array in the singleton class. Everything is being encoded fine, but I do not think they are being decoded. Here is some code:
This is my MKAnnotation class:
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#interface MapPoint : NSObject <MKAnnotation, NSCoding>
{
}
- (id)initWithAddress:(NSString*)address
coordinate:(CLLocationCoordinate2D)coordinate
title:(NSString *)t;
#property (nonatomic, readwrite) CLLocationCoordinate2D coordinate;
//This is an optional property from MKAnnotataion
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly, copy) NSString *subtitle;
#property (nonatomic) BOOL animatesDrop;
#property (nonatomic) BOOL canShowCallout;
#property (copy) NSString *address;
#property (nonatomic, copy) NSString *imageKey;
#property (nonatomic, copy) UIImage *image;
#end
#implementation MapPoint
#synthesize title, subtitle, animatesDrop, canShowCallout, imageKey, image;
#synthesize address = _address, coordinate = _coordinate;
-(id)initWithAddress:(NSString *)address
coordinate:(CLLocationCoordinate2D)coordinate
title:(NSString *)t {
self = [super init];
if (self) {
_address = [address copy];
_coordinate = coordinate;
[self setTitle:t];
NSDate *theDate = [NSDate date];
subtitle = [NSDateFormatter localizedStringFromDate:theDate
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterShortStyle];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_address forKey:#"address"];
NSLog(#"ENCODING coordLatitude %f coordLongitude %f ", _coordinate.latitude, _coordinate.longitude);
[aCoder encodeDouble:_coordinate.longitude forKey:#"coordinate.longitude"];
[aCoder encodeDouble:_coordinate.latitude forKey:#"coordinate.latitude"];
[aCoder encodeObject:title forKey:#"title"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
[self setAddress:[aDecoder decodeObjectForKey:#"address"]];
NSLog(#"DECODING coordLatitude %f coordLongitude %f ", _coordinate.latitude, _coordinate.longitude);
_coordinate.longitude = [aDecoder decodeDoubleForKey:#"coordinate.longitude"];
_coordinate.latitude = [aDecoder decodeDoubleForKey:#"coordinate.latitude"];
[self setTitle:[aDecoder decodeObjectForKey:#"title"]];
}
return self;
}
#end
Here is my singleton class:
#import <Foundation/Foundation.h>
#class MapPoint;
#interface Data : NSObject
{
NSMutableArray *_annotations;
}
#property (retain, nonatomic) NSMutableArray *annotations;
+ (Data *)singleton;
- (NSString *)pinArchivePath;
- (BOOL)saveChanges;
#end
#implementation Data
#synthesize annotations = _annotations;
+ (Data *)singleton {
static dispatch_once_t pred;
static Data *shared = nil;
dispatch_once(&pred, ^{
shared = [[Data alloc] init];
shared.annotations = [[NSMutableArray alloc]init];
});
return shared;
}
- (id)init {
self = [super init];
if (self) {
NSString *path = [self pinArchivePath];
_annotations = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
if (!_annotations) {
_annotations = [[NSMutableArray alloc]init];
}
}
return self;
}
- (NSString *)pinArchivePath {
NSArray *cachesDirectories = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDirectory = [cachesDirectories objectAtIndex:0];
return [cachesDirectory stringByAppendingPathComponent:#"pins.archive"];
}
- (BOOL)saveChanges {
NSString *path = [self pinArchivePath];
return [NSKeyedArchiver archiveRootObject:[Data singleton].annotations
toFile:path];
}
#end
In my viewDidLoad method on the map view controller, I try and place the annotations in the singleton array on the map with this:
for (MapPoint *mp in [Data singleton].annotations) {
[_worldView addAnnotation:mp];
}

The main problem is in the singleton method in these lines:
dispatch_once(&pred, ^{
shared = [[Data alloc] init];
shared.annotations = [[NSMutableArray alloc]init]; //<-- problem line
});
The shared = [[Data alloc] init]; line decodes and initializes the annotations array.
Then the shared.annotations = [[NSMutableArray alloc]init]; line re-creates and re-initializes the annotations array thus discarding the just-decoded annotations so the singleton always returns an empty array.
Remove the shared.annotations = [[NSMutableArray alloc]init]; line.
As already mentioned in the comment, the other minor issue, which causes simply confusion, is the placement of the NSLog where the coordinate is being decoded. The NSLog should be after the decode is done.

Related

create Model class to parse without any library

I want to create a model class for the json.
My JSON example is given below
json response from API:
msg = '{"type":"TYPE_NM","payload":{"responseCode":0,"nextCheckTime":30}}';
I want to create a codable(Swift) properties is like in Objective-C.
I have created two nsobject interfaces as "type" and "payload". Below I am giving my class snippets.
//msg model
#interface MessageModel : NSObject
#property (nonatomic) NSString *type;
#property (nonatomic) Payload *payload;
#end
//for payload
#interface Payload : NSObject
#property (nonatomic) NSUInteger responseCode;
#property (nonatomic) NSUInteger nextCheckTime;
#end
You can convert json string to NSDictionary object and use it to create MessageModel
Payload
#interface Payload : NSObject
#property (nonatomic) NSUInteger responseCode;
#property (nonatomic) NSUInteger nextCheckTime;
#end
#implementation Payload
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
if (self) {
_responseCode = [dict[#"responseCode"] integerValue];
_nextCheckTime = [dict[#"nextCheckTime"] integerValue];
}
return self;
}
#end
MessageModel
#interface MessageModel : NSObject
#property (nonatomic) NSString *type;
#property (nonatomic) Payload *payload;
#end
#implementation MessageModel
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
if (self) {
_type = dict[#"type"];
_payload = [[Payload alloc] initWithDictionary:dict[#"payload"]];
}
return self;
}
- (instancetype)initWithJson:(NSString *)json {
self = [super init];
if (self) {
NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
_type = dict[#"type"];
_payload = [[Payload alloc] initWithDictionary:dict[#"payload"]];
}
return self;
}
#end
Usage
NSString *input = #"{\"type\":\"TYPE_NM\",\"payload\":{\"responseCode\":0,\"nextCheckTime\":30}}";
MessageModel *model = [[MessageModel alloc] initWithJsonString:input];

Pulling coordinates for MapView annotations from MySQL database, but annotations aren't showing?

I'm trying to pull coordinates for my MapView from a MySQL database, but for some reason my coordinates just aren't showing up on the MapView?
See below my code.
MapViewController.h file
#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"
#import "JSONKit.h"
#implementation MapViewController
#synthesize mapView;
#synthesize dispensaries;
#synthesize data;
#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 = [[[serverOutput objectFromJSONString] mutableCopy] autorelease];
dispensaries = [serverOutput objectFromJSONString];
NSLog(#"%#", [serverOutput objectFromJSONString]);
for (NSDictionary *dictionary in array) {
assert([dictionary respondsToSelector:#selector(objectForKey:)]);
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];
}
MapViewAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MKAnnotation.h>
#import <CoreLocation/CoreLocation.h>
#interface MapViewAnnotation : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
}
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#end
MapViewAnnotation.m
#import "MapViewAnnotation.h"
#implementation MapViewAnnotation
#synthesize title, coordinate, subtitle;
-(void)dealloc{
[title release];
[super dealloc];
}
#end
this seems to be an issue with you expecting the wrong JSON:
You wrap the serverOutput in an array:
NSMutableArray *array = [NSMutableArray arrayWithObject:[serverOutput objectFromJSONString]];
You try to access a JKDictionary (Json Kit dictionary) WITCH is infact an array and therefore doesn't answer to objectForKey:
try to make sure:
for (NSDictionary *dictionary in array) {
assert([dictionary respondsToSelector:#selector(objectForKey:)]);
--
potential Solution
I believe your extra wrapping makes it bad. try:
NSMutableArray *array = [[[serverOutput objectFromJSONString] mutableCopy] autorelease];

Encoding a CLLocationcoordinate2D

I am trying to encode annotations that are on a map, but I read that I am not able to encode CLLocationcoordinate2D variables. Does anyone know how I can solve this? Here is some code.
This is where I drop the pins:
- (void)press:(UILongPressGestureRecognizer *)recognizer {
CGPoint touchPoint = [recognizer locationInView:_worldView];
CLLocationCoordinate2D touchMapCoordinate = [_worldView convertPoint:touchPoint toCoordinateFromView:_worldView];
geocoder = [[CLGeocoder alloc]init];
CLLocation *location = [[CLLocation alloc]initWithCoordinate:touchMapCoordinate
altitude:CLLocationDistanceMax
horizontalAccuracy:kCLLocationAccuracyBest
verticalAccuracy:kCLLocationAccuracyBest
timestamp:[NSDate date]];
[geocoder reverseGeocodeLocation:location
completionHandler:^(NSArray *placemarks, NSError *error) {
//NSLog(#"reverseGeocoder:completionHandler: called");
if (error) {
//NSLog(#"Geocoder failed with error: %#", error);
} else {
CLPlacemark *place = [placemarks objectAtIndex:0];
geocodedAddress = [NSString stringWithFormat:#"%# %#, %# %#", [place subThoroughfare], [place thoroughfare], [place locality], [place administrativeArea]];
if (UIGestureRecognizerStateBegan == [recognizer state]) {
value = [number intValue];
number = [NSNumber numberWithInt:value + 1];
_addressPin = [[MapPoint alloc]initWithAddress:geocodedAddress coordinate:touchMapCoordinate
title:geocodedAddress identifier:number];
NSLog(#"The identifier is %#", number);
[[Data singleton].annotations addObject:_addressPin];
[_worldView addAnnotation:_addressPin];
NSLog(#"The number of pins in the annotation array is: %u",[Data singleton].annotations.count);
}
}
}];
}
Here is my class that conforms to the MKAnnotation protcol:
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#interface MapPoint : NSObject <MKAnnotation, NSCoding>
{
}
- (id)initWithAddress:(NSString*)address
coordinate:(CLLocationCoordinate2D)coordinate
title:(NSString *)t
identifier:(NSNumber *)ident;
//This is a required property from MKAnnotation
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
//This is an optional property from MKAnnotataion
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly, copy) NSString *subtitle;
#property (nonatomic) BOOL animatesDrop;
#property (nonatomic) BOOL canShowCallout;
#property (copy) NSString *address;
#property (copy) NSNumber *identifier;
#property (nonatomic, copy) NSString *imageKey;
#property (nonatomic, copy) UIImage *image;
#end
import "MapPoint.h"
#implementation MapPoint
#synthesize title, subtitle, animatesDrop, canShowCallout, imageKey, image;
#synthesize address = _address, coordinate = _coordinate, identifier = _indentifier;
-(id)initWithAddress:(NSString *)address
coordinate:(CLLocationCoordinate2D)coordinate
title:(NSString *)t
identifier:(NSNumber *)ident {
self = [super init];
if (self) {
_address = [address copy];
_coordinate = coordinate;
_indentifier = ident;
[self setTitle:t];
NSDate *theDate = [NSDate date];
subtitle = [NSDateFormatter localizedStringFromDate:theDate
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterShortStyle];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_address forKey:#"address"];
[aCoder encodeObject:title forKey:#"title"];
[aCoder encodeObject:_indentifier forKey:#"identifier"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
[self setAddress:[aDecoder decodeObjectForKey:#"address"]];
}
return self;
}
#end
Just encode the two fields of the CLLocationCoordinate2D value.
[aCoder encodeDouble:_coordinate.latitude forKey:#"latitude"];
[aCoder encodeDouble:_coordinate.longitude forKey:#"longitude"];
NSValue is NSCoding compliant. You can box your CLLocationcoordinate2D variable in an NSValue object:
[coder encodeObject:[NSValue valueWithMKCoordinate:coordinate] forKey:#"coordinate"]
The CLLocationCoordinate2D's latitude and longitude are of type CLLocationDegrees which is, essentially, a fancy way of saying double.
This is how you can encode and decode them:
NSString *const kPinCoordinateLatitudeKey = #"kPinCoordinateLatitudeKey";
NSString *const kPinCoordinateLongitudeKey = #"kPinCoordinateLongitudeKey";
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeDouble:self.coordinate.latitude forKey:kPinCoordinateLatitudeKey];
[encoder encodeDouble:self.coordinate.longitude forKey:kPinCoordinateLongitudeKey];
}
- (id)initWithCoder:(NSCoder *)decoder
{
if((self = [super init])) {
CLLocationDegrees latitude = [decoder decodeDoubleForKey:kPinCoordinateLatitudeKey];
CLLocationDegrees longitude = [decoder decodeDoubleForKey:kPinCoordinateLongitudeKey];
_coordinate = CLLocationCoordinate2DMake(latitude, longitude);
}
return self;
}
I decided to encode use a CLLocation property handle this situation, which conforms to NSSecureCoding.
If you need to convert to or from a CLLocationCoordinate2D:
// Coordinate to Location
CLLocationCoordinate2D coord;
CLLocation *loc = [[CLLocation alloc] initWithLatitude:coord.latitude
longitude:coord.longitude];
// Location to Coordinate
CLLocationCoordinate2D coord = loc.coordinate;

Saving the title of a button so it can be accessed in another view (Objective-C)

I'm trying to save the name of a button using a singleton so that the name can be accessed in another view to play a video with the same name. However, I'm getting the error: SIGABRT. I don't really see what's wrong with my code. Any ideas?
#import "List.h"
#import "MyManager.h"
#import "Video.h"
#implementation ExerciseList
-(IBAction) goToVideo:(UIButton *) sender{
MyManager *sharedManager = [MyManager sharedManager];
sharedManager.vidName = [[sender titleLabel] text];
Video *videoGo = [[Video alloc] initWithNibName: #"Video" bundle: nil];
[self.navigationController pushViewController: videoGo animated: YES];
[videoGo release];
}
Here is my .h and .m for MyManager:
#import <foundation/Foundation.h>
#interface MyManager : NSObject {
NSMutableArray *workouts;
NSString *vidName;
}
#property (nonatomic, retain) NSMutableArray *workouts;
#property (nonatomic, retain) NSString *vidName;
+ (id)sharedManager;
#end
#import "MyManager.h"
static MyManager *sharedMyManager = nil;
#implementation MyManager
#synthesize workouts;
#synthesize vidName;
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
- (id)init {
if ((self = [super init])) {
workouts = [[NSMutableArray alloc] init];
vidName = [[NSString alloc] init];
}
return self;
}
-(void) dealloc{
self.workouts = nil;
self.vidName = nil;
[super dealloc];
}
#end
You should access the title of the button
sharedManger.vidName = [sender currentTitle];
However you are not using ARC so also check where your vidName property is retain or copy.
if it is not retain or copy then you can use this code also
if(sharedManger.vidname != nil){
[sharedManger.vidName release];
sharedManger.vidName = nil;
}
sharedManger.vidName = [[sender currentTitle] retain];

MKAnnotationView's subtitle not getting reloaded

I currently have an XML model getting processed my an NSXMLParser; I get about 340 objects in my model after processing. I place all of the objects on my MKMapView. Once a user "selects" an object (MKAnnotationViewPin); once I start off, the title is populated, coords too (duh?) but the subtitle is set to a place-holder. I process another XML file to retrieve extra information, the subtitle gets updated and populated.
Once the XML file gets parsed I get notified and update its <MKAnnotation> object to reflect the changed subtitle. To reflect the change on the map I have to "unselect" the pin and click it again for it to show the change.
Here is my <MKAnnotation> object:
Header:
#interface CPCustomMapPin : NSObject <MKAnnotation>
{
NSString *_title;
NSString *_annotation;
CLLocationCoordinate2D _coords;
NSString *stationID;
}
#property (nonatomic, retain) NSString *_title;
#property (nonatomic, retain) NSString *_annotation;
#property (nonatomic) CLLocationCoordinate2D _coords;
#property (nonatomic, retain) NSString *stationID;
- (id) initWithTitle: (NSString *) _title withAnnotation: (NSString *) _annotation withCoords: (CLLocationCoordinate2D) _coords withStationID: (NSString *) _id;
Implementation:
#implementation CPCustomMapPin
#synthesize _title, _annotation, _coords, stationID;
- (id) initWithTitle: (NSString *) __title withAnnotation: (NSString *) __annotation withCoords: (CLLocationCoordinate2D) __coords withStationID: (NSString *) _id
{
_title = [[NSString alloc] init];
_annotation = [[NSString alloc] init];
stationID = [[NSString alloc] init];
[self set_title: __title];
[self set_annotation: __annotation];
[self set_coords: __coords];
[self setStationID: _id];
return self;
}
- (NSString *) title
{
return _title;
}
- (NSString *) subtitle
{
return _annotation;
}
- (CLLocationCoordinate2D) coordinate
{
return _coords;
}
- (NSString *) description
{
return [NSString stringWithFormat: #"title: %# subtitle: %# id: %#", _title, _annotation, stationID];
}
- (void) dealloc
{
[_title release];
[_annotation release];
[stationID release];
[super dealloc];
}
#end
Thank you for your valuable input.
Apparently no one knows… I just found this technique:
- (void) closeAnnotation: (id <MKAnnotation>) annotation inMapView: (MKMapView *) mapView
{
[mapView deselectAnnotation: annotation animated: NO];
[mapView selectAnnotation: annotation animated: YES];
}
And of course you call your method accordingly:
Example:
- (void) myMethod
{
for (id <MKAnnotation> _annotation in mapView.annotations)
{
[self closeAnnotation: _annotation inMapView: mapView];
}
}