From 4871a3e149a7119af3e886760ca294301e2bee8b Mon Sep 17 00:00:00 2001 From: Jason David Miller Date: Sat, 23 Sep 2023 18:59:29 -0700 Subject: [PATCH] Add search & forwardGeocode --- ios/Classes/TiMapModule.m | 145 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 4 deletions(-) diff --git a/ios/Classes/TiMapModule.m b/ios/Classes/TiMapModule.m index a9768c9c..5456c4a2 100644 --- a/ios/Classes/TiMapModule.m +++ b/ios/Classes/TiMapModule.m @@ -10,6 +10,7 @@ #import "TiBlob.h" #import "TiMapCameraProxy.h" #import "TiMapConstants.h" +#import "TiMapUtils.h" #import "TiMapViewProxy.h" @implementation TiMapModule @@ -89,16 +90,12 @@ - (void)openLookAroundDialog:(id)args return; } - KrollCallback *callback = (KrollCallback *)args[@"callback"]; CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([TiUtils doubleValue:args[@"latitude"]], [TiUtils doubleValue:args[@"longitude"]]); MKLookAroundSceneRequest *request = [[MKLookAroundSceneRequest alloc] initWithCoordinate:coordinate]; [request getSceneWithCompletionHandler:^(MKLookAroundScene *_Nullable_result scene, NSError *_Nullable error) { if (error != nil) { - [callback call:@[ @{@"success" : @(NO), - @"error" : error.localizedDescription} ] - thisObject:self]; return; } @@ -120,6 +117,142 @@ - (void)lookAroundViewControllerDidPresentFullScreen:(MKLookAroundViewController #endif +- (MKLocalSearchCompleter *)searchCompleter +{ + if (_searchCompleter == nil) { + _searchCompleter = [[MKLocalSearchCompleter alloc] init]; + _searchCompleter.delegate = self; + } + + return _searchCompleter; +} + +- (void)search:(id)args +{ + ENSURE_UI_THREAD(search, args); + + NSString *value = [TiUtils stringValue:args[0]]; + + // Require a search value + if (!value) { + [self throwException:@"Missing required search value" subreason:@"Please provide the value as a String" location:CODELOCATION]; + return; + } + + // Pass additional options like search region + if ([args count] > 1) { + NSDictionary *options = (NSDictionary *)args[1]; + if (options == nil) { + [self throwException:@"Options have to be called within an Object" subreason:@"Please provide the value as an Object" location:CODELOCATION]; + } + + // Handle search region + if (options[@"region"]) { + NSDictionary *region = options[@"region"]; + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([TiUtils doubleValue:region[@"latitude"]], [TiUtils doubleValue:region[@"longitude"]]); + MKCoordinateSpan span = MKCoordinateSpanMake([TiUtils doubleValue:region[@"latitudeDelta"]], [TiUtils doubleValue:region[@"longitudeDelta"]]); + + if (CLLocationCoordinate2DIsValid(coordinate)) { + [[self searchCompleter] setRegion:MKCoordinateRegionMake(coordinate, span)]; + } + } + + // Handle filter types + if ([TiUtils isIOSVersionOrGreater:@"13.0"] && options[@"resultTypes"]) { + if (@available(iOS 13.0, *)) { + _searchCompleter.resultTypes = [TiMapUtils mappedResultTypes:options[@"resultTypes"]]; + } else { + NSLog(@"[ERROR] The \"resultTypes\" options are only available on iOS 13+"); + } + } + } + + [[self searchCompleter] setQueryFragment:value]; +} + +- (void)geocodeAddress:(id)args +{ + NSString *address = (NSString *)args[0]; + KrollCallback *callback = (KrollCallback *)args[1]; + + CLGeocoder *geocoder = [[CLGeocoder alloc] init]; + [geocoder geocodeAddressString:address + completionHandler:^(NSArray *_Nullable placemarks, NSError *_Nullable error) { + if (placemarks.count == 0 || error != nil) { + [callback call:@[ @{@"success" : @(NO), + @"error" : error.localizedDescription ?: @"Unknown error"} ] + thisObject:self]; + return; + } + + CLPlacemark *place = placemarks[0]; + + NSDictionary *proxyPlace = @{ + @"name": NULL_IF_NIL(place.name), + @"street": NULL_IF_NIL([self formattedStreetNameFromPlace:place]), + @"thoroughfare": NULL_IF_NIL(place.thoroughfare), + @"subThoroughfare": NULL_IF_NIL(place.subThoroughfare), + @"postalCode": NULL_IF_NIL(place.postalCode), + @"city": NULL_IF_NIL(place.locality), + @"subLocality": NULL_IF_NIL(place.subLocality), + @"country": NULL_IF_NIL(place.country), + @"state": NULL_IF_NIL(place.administrativeArea), + @"subAdministrativeArea": NULL_IF_NIL(place.subAdministrativeArea), + @"latitude": @(place.location.coordinate.latitude), + @"longitude": @(place.location.coordinate.longitude), + }; + + [callback call:@[ @{@"success" : @(YES), + @"place" : proxyPlace} ] + thisObject:self]; + }]; +} + +- (NSString *)formattedStreetNameFromPlace:(CLPlacemark *)place +{ + if (place.thoroughfare == nil) { + return nil; + } else if (place.subThoroughfare == nil) { + return place.thoroughfare; + } + + return [NSString stringWithFormat:@"%@ %@", place.thoroughfare, place.subThoroughfare]; +} + +- (void)completer:(MKLocalSearchCompleter *)completer didFailWithError:(NSError *)error +{ + [self fireEvent:@"didUpdateResults" withObject:@{ @"results" : @[], @"error" : error.localizedDescription }]; +} + +- (void)completerDidUpdateResults:(MKLocalSearchCompleter *)completer +{ + NSMutableArray *> *proxyResults = [NSMutableArray arrayWithCapacity:completer.results.count]; + + [completer.results enumerateObjectsUsingBlock:^(MKLocalSearchCompletion *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + NSMutableArray *> *titleHighlightRanges = [NSMutableArray arrayWithCapacity:obj.titleHighlightRanges.count]; + NSMutableArray *> *subtitleHighlightRanges = [NSMutableArray arrayWithCapacity:obj.subtitleHighlightRanges.count]; + + [obj.titleHighlightRanges enumerateObjectsUsingBlock:^(NSValue *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + [titleHighlightRanges addObject:@{@"offset" : @(obj.rangeValue.location), + @"length" : @(obj.rangeValue.length)}]; + }]; + + [obj.subtitleHighlightRanges enumerateObjectsUsingBlock:^(NSValue *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) { + [subtitleHighlightRanges addObject:@{@"offset" : @(obj.rangeValue.location), + @"length" : @(obj.rangeValue.length)}]; + }]; + + [proxyResults addObject:@{ + @"title" : obj.title, + @"subtitle" : obj.subtitle, + @"titleHighlightRanges" : titleHighlightRanges, + @"subtitleHighlightRanges" : subtitleHighlightRanges + }]; + }]; + + [self fireEvent:@"didUpdateResults" withObject:@{ @"results" : proxyResults }]; +} + MAKE_SYSTEM_PROP(STANDARD_TYPE, MKMapTypeStandard); MAKE_SYSTEM_PROP(NORMAL_TYPE, MKMapTypeStandard); // For parity with Android MAKE_SYSTEM_PROP(SATELLITE_TYPE, MKMapTypeSatellite); @@ -168,4 +301,8 @@ - (void)lookAroundViewControllerDidPresentFullScreen:(MKLookAroundViewController MAKE_SYSTEM_PROP(FEATURE_POINT_OF_INTEREST, MKMapFeatureOptionPointsOfInterest); #endif +MAKE_SYSTEM_PROP(SEARCH_RESULT_TYPE_ADDRESS, MKLocalSearchCompleterResultTypeAddress); +MAKE_SYSTEM_PROP(SEARCH_RESULT_TYPE_POINT_OF_INTEREST, MKLocalSearchCompleterResultTypePointOfInterest); +MAKE_SYSTEM_PROP(SEARCH_RESULT_TYPE_QUERY, MKLocalSearchCompleterResultTypeQuery); + @end