Whatever address i give to the geocoder ([geocoder geocodeAddressString:completionHandler:), it always puts only one object in the placemarks array.
I there any way to get multiple results (like in Maps app) from which the user can select one?
Apple's native geocoding service is provided by the MapKit framework. The important object in this framework is MKLocalSearch, which can geocode addresses and return multiple results.
MKLocalSearch returns back 10 results in mapItems of type MKMapItem. Each MKMapItem contains a MKPlacemark object, which is a subclass of CLPlacemark.
Here's an example using MapKit's MKLocalSearch:
MKLocalSearchRequest* request = [[MKLocalSearchRequest alloc] init];
request.naturalLanguageQuery = #"Calgary Tower";
request.region = MKCoordinateRegionMakeWithDistance(loc, kSearchMapBoundingBoxDistanceInMetres, kSearchMapBoundingBoxDistanceInMetres);
MKLocalSearch* search = [[MKLocalSearch alloc] initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
yourArray = response.mapItems; // array of MKMapItems
// .. do you other logic here
}];
I've done some sniffing on the packets and it seems that CLGeocoder doesn't connect to Google's geocoding service, but to Apple's. I've also noticed that I get only one placemark from there every time.
If you want something more sophisticated you should use Google's or other geocoding. I use SVGeocoder (https://github.com/samvermette/SVGeocoder), which has a very similar API to CLGeocoder.
Related
I'm downloading often identical data on multiple calls to different URLs in RESTKit. The first call maps fine, but the subsequent calls then replace the first call's entities.
The behaviour that I want is to have the objects unique to within their parent entity, so even if the data looks the same I still want a new object to be created, but at the moment it looks like RESTKit wants them to be unique across the entire database. There's no unique key to do this in the data I'm downloading, the objects are literally identical. Below is the code I'm using to create the operation. How do I set this up to allow duplicates?
NSMutableURLRequest *request = [self requestWithURL:URL];
[request setHTTPMethod:#"GET"];
RKHTTPRequestOperation *requestOperation = [[RKHTTPRequestOperation alloc]initWithRequest:request];
RKResponseDescriptor *responseDescriptor = [INKResponseDescriptorFactory journeySegmentDescriptorForPath:URL.path inStore:[RKObjectManager sharedManager].managedObjectStore];
RKManagedObjectRequestOperation *operation = [[RKManagedObjectRequestOperation alloc] initWithHTTPRequestOperation:requestOperation
responseDescriptors:#[responseDescriptor]];
operation.managedObjectContext = [self.objectManager context];
operation.managedObjectCache = self.objectManager.managedObjectStore.managedObjectCache;
operation.savesToPersistentStore = NO;
[operation setCompletionBlockWithSuccess: ^(RKObjectRequestOperation *requestOperation, RKMappingResult *mappingResult)
{
success();
} failure: ^(RKObjectRequestOperation *requestOperation, NSError *error) {
failure(error);
}];
[self.objectManager enqueueObjectRequestOperation:operation];
Figured out a way to do this.
Each time I'm updating the data, even though the entities I get contain identical data, I'm retrieving them from a different url.
RESTKit allows you to retrieve metadata from you calls and map this into your managed objects properties.
So what I've done is map the URL I used for the request into a property and used that as well as an identifier, which is unique within this call's returned objects, to make objects which will be unique throughout the database.
So my mapping now looks like this:
RKEntityMapping *seatMapping = [RKEntityMapping mappingForEntityForName:#"Seat" inManagedObjectStore:store];
[seatMapping addAttributeMappingsFromDictionary:#{ #"designator" : #"designator",
#"status" : #"status",
#"#metadata.HTTP.request.URL" : #"requestURL"}];
seatMapping.identificationAttributes = #[#"requestURL", #"designator"];
I'm using the Standard Matchmaking User Interface and two iPads iOS6. The problem is when I create a new match in the first device, the second device supposed to see the existing match when I view the Matchmaking User Interface but it's not. I'm sure my code is correct. here is the method:
-(void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController*)viewController {
// the view that will present Matchmaker View Controller
self.viewCaller = viewController;
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
GKTurnBasedMatchmakerViewController *viewMatchmaker = [[GKTurnBasedMatchmakerViewController alloc] initWithMatchRequest:request];
viewMatchmaker.turnBasedMatchmakerDelegate = self;
viewMatchmaker.showExistingMatches = YES;
[self.viewCaller presentViewController:viewMatchmaker animated:YES completion:nil];
}
I even tried on two different sample games but the same problem, so how can I solve this ?
When Gamekit does matchmaker for a turnbase match it is trying to find a match with an open slot to fill the player in. If it cannot find an open slot to fill the player in it then creates a new match.
Try on one device to create a match and go through one turn with that device, that is when you call...
endTurnWithNextParticipants:turnTimeout:matchData:completionHandler:
Then on the other device try looking for a match.
What are the delete semantics for RestKit when using it with Core Data?
For example, assume I correctly set a primaryKeyAttribute in RestKit for Organization entities. If I do a GET on, say, /organizations/ I get entries for /organizations/1/, /organizations/2/, and /organizations/3/ back. Let's say I do a GET on /organizations/ a bit later and only get entries for /organizations/1/ and /organizations/3/ back. `/organizations/2/ has been deleted on the server.
I would expect RestKit to delete my Core Data record for /organizations/2/. Is this what RestKit does or do I have to implement this behavior? Does this change in any way if I am using the reboot-networking-layer branch? Are there any settings in RestKit I should be aware of that affect this behavior?
You need to implement FetchRequests in order for those objects to be deleted. Here is a simple example:
[objectManager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:#"/organizations"];
NSDictionary *argsDict = nil;
BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
if (match) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Organization"];
return fetchRequest;
}
return nil;
}];
I do this in the ApplicationDelegate(the same place I setup RestKit). After you do it there, all other calls will automatically delete orphaned objects.
I have an entity called Practice and I use a View Controller called SelectorViewController to select one of the practices, selectedPractice. I then return selectedPractice to a view Controller called RegularViewController where I display some of the selectedPractice attributes. All of this works fine. However the app has a number of other View Controllers which can be reached by modal segues from instances of RegularViewController. As a result, if I leave and then come back to RegularViewController, selectedPractice is reset as null. I would also like to save selectedPractice so that it is available at app initialisation if it has previously been set in SelectorViewController. How do I achieve this by making selectedPractice persistent across the app, and available at runtime?
Regards
Thanks to the post above, which was great, I managed to sort it. Here is my code, which may be very clumsy, but it works.
Firstly, as I loaded the fetchedObjects into a PickerView in SelectorView Controller, I set an attribute "isSelectedPractice" to "NO" with the following code:
for (Practice *fetchedPractice in [self.fetchedResultsController fetchedObjects]) {
[fetchedPractice setValue:#"NO" forKey:#"isSelectedPractice"];
[self.managedObjectContext save:nil];
I then identified for the selected Practice:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
Practice *practice = [[self.fetchedResultsController fetchedObjects] objectAtIndex:row];
self.selectedPractice = practice;
NSLog(#"The '%#' practice was selected using the picker", self.selectedPractice.name);
}
as the view Segue'd back to RegularViewController I set the isSelectedPractice attribute for selectedPractice to YES. I kept it this late as I didn't want more than one selection in the PickerView to result in multiple objects with isSelectedPractice YES.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SavedPractice Segue"])
{
[self.selectedPractice setValue:#"YES" forKey:#"isSelectedPractice"];
[self.managedObjectContext save:nil];
NSLog(#"Setting SelectedPractice as '%#' in RegularViewController with isSelectedPractice as '%#'",self.selectedPractice.name,self.selectedPractice.isSelectedPractice );
RegularViewController *rvc= segue.destinationViewController;
rvc.delegate = self;
rvc.selectedPractice = self.selectedPractice;
}
else {
NSLog(#"Unidentified Segue Attempted!");
}
}
I then set the following Predicate in the setupFetchedResultsController method of RegularViewController:
request.predicate = [NSPredicate predicateWithFormat:#"isSelectedPractice = %#", #"YES"];
Many thanks for the help
Without seeing your actual project, one way I know will work but might be a little too round a bout would be to add an attribute "isSelectedPractice" to your entity. You could make it a BOOL, but I've had mixed results with BOOL's in Core Data, I prefer to just leave it as a NSString and set it to "yes" or "no". Then when you pull it down, modify it or add it to core Data as a entity with isSelectedPractice set to "yes". Then in your other controllers, do a
if (self.managedObjectContext == nil) {
self.managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
then do a fetch request to get entities with a predicate which is looking for isSelectedPractice equaling "yes". If you need actual code samples on how to do this let me know and I'll edit them in.
Is there a way to inspect a NSPredicate object for the purposes of serializing it into a URL? I am trying to retrieve the data remotely and need to translate the predicate object into a URL with querystring parameters that the server understands.
This was inspired by a talk given in WWDC 2010 called "Building a Server Driven User EXperience" where the speakers talk about using Core-Data and with a server backend. I have followed the session video and slides, but am stuck on the serializing point. For example, there is a Person object, and I'm trying to fetch all people whose first name is "John". I am using a subclass of NSManagedObjectContext called RemoteManagedObjectContext, which overrides the executeFetchRequest method, and is supposed to send the call to the server instead. The fetch request is being created as (ellipsed non-essential parts):
#implementation PeopleViewController
- (NSArray *)getPeople {
RemoteFetchRequest *fetchRequest = [[RemoteFetchRequest alloc] init];
NSEntityDescription *entity = ...
NSPredicate *template = [NSPredicate predicateWithFormat:
#"name == $NAME AND endpoint = $ENDPOINT"];
NSPredicate *predicate = [template predicateWithSubstitutionVariables:...];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
// the custom subclass of NSManagedObjectContext executes this
return [remoteMOC executeFetchRequest:fetchRequest error:&error];
}
#end
Now inside the custom subclass of NSManagedObjectContext, how can I serialize the fetch request into querystring parameters suitable for the server. So given the above fetch request, the corresponding URL would be:
http://example.com/people?name=John
It is possible to get a string representation of the predicate which returns,
name == "John" AND endpoint == "people"
that I can parse to get the parameters name, and endpoint. However, is it possible to do it without parsing the string? Here's a partial implementation of the RemoteManagedObjectContext class.
#implementation RemoteManagedObjectContext
- (NSArray *)executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error {
// this gives name == "John" AND endpoint == "people"
// don't know how else to retrieve the predicate data
NSLog(#"%#", [[request predicate] predicateFormat]);
...
}
#end
Even better than a string representation is an object-oriented representation! And it's done automatically!
First, check the class of the NSPredicate. It will be an NSCompoundPredicate. Cast it to an appropriate variable.
You'll then see that it's compoundPredicateType is NSAndPredicateType, just like you'd expect.
You can also see that the array returned by -subpredicates reveals 2 NSComparisonPredicates.
The first subpredicate has a left expression of type NSKeyPathExpressionType and a -keyPath of #"name", the operator is NSEqualToPredicateOperatorType. The right expression will be an NSExpression of type NSConstantValueExpressionType, and the -constantValue will be #"John".
The second subpredicate will be similar, except that the left expression's keyPath will be #"endpoint", and the right expression's constantValue will be #"people".
If you want more in-depth information on turning NSPredicates into an HTTP Get request, check out my StackOverflow framework, "StackKit", which does just that. It's basically a framework that behaves similarly to CoreData, but uses StackOverflow.com (or any other stack exchange site) to retrieve information. Underneath, it's doing a lot to convert NSPredicate objects into a URL. You're also welcome to email me any specific questions you have.