Search code examples
iosmapkitmapkitannotation

IOS/Objective-C: Location pins (json) not showing up after application first launch


My app receives a json object the first time is executed (with three pin point locations); there is a mapKit (the first screen) and a TableView where the user can check those locations. The issue is that when I first launch the app, there are no pins on the map. But if I switch to the table I can see them - on the cells - and if I switch again to the map, the pins appear...I don't Know why this happens, shouldn't I see the pins right after the app launch? The Map code:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSNotificationCenter *notification=[NSNotificationCenter defaultCenter];
    [notification addObserver:self selector:@selector (receiveNotification:) name:@"notification" object:self];

    _mapView.showsUserLocation=YES;
    _mapView.showsBuildings=YES;
    _locationManager = [[CLLocationManager alloc] init];
    [_locationManager requestAlwaysAuthorization]; 
    _mapView.delegate = self;
    _locationManager.delegate=self;

}

-(void)viewDidAppear:(BOOL)animated{
        [self receiveNotification:nil];


}
-(void)receiveNotification:(NSNotification*)notification{
    NSArray *spots = [Spot spotType:@"users"];
    NSArray *places = [Spot spotWithType:@"users"];

    [_mapView addAnnotations:spots];
    [_mapView addAnnotations:places];

}

And the table:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.dataSource = self;

    self.detailList=@[@"Your Favourite Spots",@"Our suggestion"];

}

-(void)viewDidAppear:(BOOL)animated{
    _lisbonSpots = [[Spot spotType:@"users"]mutableCopy];
    _users=[[Spot spotWithType:@"users"]mutableCopy];

    [self.tableView reloadData];

}

EDIT - The Spot Class

@implementation Spot


@dynamic ID;
@dynamic name;
@dynamic desc;
@dynamic type;
@dynamic phone;
@dynamic latitude;
@dynamic longitude;

+ (instancetype)spotWithName:(NSString *)name andCoord:

(CLLocationCoordinate2D)coord type:(NSString*)type desc:(NSString*)desc phone:(NSString*)phone{

    NSPersistentContainer *persistenceContainer = [AppDelegate sharedDelegate].persistentContainer;
    NSManagedObjectContext *context = persistenceContainer.viewContext;

    Spot *spot = [NSEntityDescription insertNewObjectForEntityForName:@"Spot" inManagedObjectContext:context];

    spot.name = name;
    spot.latitude = coord.latitude;
    spot.longitude = coord.longitude;
    spot.type=type;
    spot.desc=desc;
    spot.phone=phone;


    [[AppDelegate sharedDelegate] saveContext];

    return spot;
}

+ (instancetype)spotWithDict:(NSDictionary *)dict {
    CLLocationCoordinate2D coord = CLLocationCoordinate2DMake([dict[@"latitude"] doubleValue], [dict[@"longitude"] doubleValue]);

    return [Spot spotWithName:dict[@"name"] andCoord:coord type:dict[@"type"] desc:dict[@"desc"] phone:dict[@"phone"]];
}

+ (NSArray*)getSpotType:(NSString*)type withPredicate:(NSString*) pred andMessage:(NSString*)message {

    NSPersistentContainer *persistenceContainer = [AppDelegate sharedDelegate].persistentContainer;

    NSPredicate* predicate = [NSPredicate predicateWithFormat:pred, type];

    NSManagedObjectContext *context = persistenceContainer.viewContext;

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Spot"];

    [request setPredicate:predicate];

    NSError *error;

    NSArray *result = [context executeFetchRequest:request error:&error];

    if (error != nil) {
        NSLog(message, [error localizedDescription]);
        return nil;
    }
    return result;
}

+ (NSArray*)spotType:(NSString*)type {
    return [Spot getSpotType:type withPredicate:@"type =%@" andMessage:@"[Spot spotType] -> %@"];
}

+ (NSArray*)spotWithType:(NSString*)type {
    return [Spot getSpotType:type withPredicate:@"NOT (type = %@)" andMessage:@"[Spot spotWithType] -> %@"];
}


- (CLLocationCoordinate2D)coordinate {
    return CLLocationCoordinate2DMake(self.latitude, self.longitude);
}

- (NSString *)title {
    return self.name;
}

- (NSString *)description {

    return [NSString stringWithFormat:@"%@", self.name];
}

@end

EDIT: The SpotService class

@implementation SpotService

+ (NSURL *)serviceURL {
    return [NSURL URLWithString:@"http://training.reativ.io/ios/lisbon-spots"];

}
+ (BOOL)service:(id<SpotServiceInvoker>)invoker {

    NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:[SpotService serviceURL]];


    NSURLSession *session = [NSURLSession sharedSession];

    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

        if (error != nil) {
            NSLog(@"Response: %@", response);
            NSLog(@"Error: %@", error);

            return;
        }

        NSArray *lisbonSecrets = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];


        dispatch_async(dispatch_get_main_queue(), ^{
            if ([invoker respondsToSelector:@selector(receiveSpot:)]){
                [invoker receiveSpot:lisbonSecrets];
            }

            for(NSDictionary *dict in lisbonSecrets) {
                [Spot spotWithDict:dict];
            }
        });
    }];

    [task resume];

    return YES;
}

Solution

  • My guess is - your Spot class retrieve data asynchronously and when you call [Spot spotType:@"users"] for the first time from viewDidAppear on your MapView there is no data retrieved yet. When you switch view controller the data appears and the everything works smoothly.

    But it's better to show us your Spot class. Probably your need a completion handler or something like this to achieve expected behaviour.

    Also, you call addAnnotations every time when your map appears on the screen and it means that MKMapView will add a copy of the annotations each time your call this methods. It's better to add additional checks to be sure that you do not add the same annotations more than once.