From 6b5bdbc74b411ed12bab7535253f8dbae17aa098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Fri, 22 Sep 2023 21:23:50 +0200 Subject: [PATCH 1/5] feat(ios): add new search completion APIs --- apidoc/Map.yml | 138 +++++++++++++++++++++++++------- ios/Classes/TiMapModule.h | 3 +- ios/Classes/TiMapModule.m | 117 +++++++++++++++++++++++++++ ios/Classes/TiMapModuleAssets.m | 4 +- ios/manifest | 2 +- 5 files changed, 230 insertions(+), 34 deletions(-) diff --git a/apidoc/Map.yml b/apidoc/Map.yml index 9cf981a6..d26e601d 100644 --- a/apidoc/Map.yml +++ b/apidoc/Map.yml @@ -42,13 +42,14 @@ description: | ``` - - Instantiate the module with the `require('ti.map')` method, then make subsequent API calls with + - Instantiate the module with the `import Map from 'ti.map'` API, then make subsequent API calls with the new map object. ``` javascript - var Map = require('ti.map'); - var mapview = Map.createView({ - mapType: Map.NORMAL_TYPE + import Map from 'ti.map'; + + const mapView = Map.createView({ + mapType: Map.NORMAL_TYPE }); ``` @@ -99,12 +100,13 @@ description: | ``` - - Instantiate the module with the `require('ti.map')` method, then make subsequent API calls with + - Instantiate the module with the `import Map from 'ti.map'` API, then make subsequent API calls with the new map object. ``` javascript - var Map = require('ti.map'); - var mapview = Map.createView({ + import Map from 'ti.map'; + + const mapView = Map.createView({ mapType: Map.NORMAL_TYPE }); ``` @@ -495,6 +497,43 @@ methods: summary: Returns a code to indicate whether Google Play Services is available on the device. since: "3.1.1" platforms: [android] + + - name: search + summary: | + Uses the native `MKLocalSearchCompleter` class to search places for + a given input value. + description: | + Please use the `didUpdateResults` event to get updates for a search + completion request via the `results` field. If the search failed, the + `results` field is empty and an `error` is provided. + parameters: + - name: value + summary: The value to search with. + type: String + - name: options + summary: Additional options to fine-tune the search request. + type: SearchCompletionOptions + platforms: [iphone, ipad, macos] + since: "12.3.0" + + - name: geocodeAddress + summary: | + Resolve address details using the `CLGeocoder` to get information (e.g. + latitude, longitude, postal code and city) about a given input address. + description: | + The result is provided via the callback (second function argument). + parameters: + - name: address + summary: The address to resolve. + type: String + - name: callback + summary: | + Function to be called upon completion (either success with a place + or an error). + type: Callback + optional: false + platforms: [iphone, ipad, macos] + since: "12.3.0" - name: getLookAroundImage summary: A utility function that you use to create a static image from a LookAround scene. @@ -539,10 +578,11 @@ examples: and it is not practical to identify them by title. ``` javascript - var Map = require('ti.map'); - var win = Titanium.UI.createWindow(); + import Map from 'ti.map'; + + const window = Ti.UI.createWindow(); - var mountainView = Map.createAnnotation({ + const mountainView = Map.createAnnotation({ latitude: 37.390749, longitude: -122.081651, title: 'Appcelerator Headquarters', @@ -551,7 +591,7 @@ examples: myid: 1 // Custom property to uniquely identify this annotation. }); - var mapview = Map.createView({ + const mapView = Map.createView({ mapType: Map.NORMAL_TYPE, region: { latitude: 33.74511, @@ -565,7 +605,7 @@ examples: annotations: [ mountainView ] }); - var circle = Map.createCircle({ + const circle = Map.createCircle({ center: { latitude: 33.74511, longitude: -84.38993 @@ -573,14 +613,15 @@ examples: radius: 1000, // = 1.0 km fillColor: '#20FF0000' }); - mapview.addCircle(circle); - win.add(mapview); + mapView.addCircle(circle); + window.add(mapView); - mapview.addEventListener('click', function(event) { + mapView.addEventListener('click', event => { Ti.API.info('Clicked ' + event.clicksource + ' on ' + event.latitude + ', ' + event.longitude); }); - win.open(); + + windown.open(); ``` - title: Alloy XML Markup example: | @@ -605,7 +646,7 @@ examples: ``` xml - + @@ -615,7 +656,7 @@ examples: `app/styles/index.tss`: ``` javascript - "#mapview": { + "#mapView": { region: { latitude: 33.74511, longitude: -84.38993, @@ -651,11 +692,12 @@ examples: view instance. ``` javascript - var Map = require('ti.map'); - var win = Titanium.UI.createWindow(); - var annotations = []; + import Map from 'ti.map'; + + const window = Ti.UI.createWindow(); + const annotations = []; - for (var i = 0; i < 10; i++) { + for (let i = 0; i < 10; i++) { annotations.push(Map.createAnnotation({ title: 'Appcelerator Inc.', subtitle: 'TiRocks!', @@ -675,7 +717,7 @@ examples: })); } - var mapview = Map.createView({ + const mapView = Map.createView({ annotations: annotations, rotateEnabled: true, mapType: Map.MUTED_STANDARD_TYPE, @@ -683,23 +725,37 @@ examples: userLocation: true }); - mapview.addEventListener('clusterstart', function(e) { + mapView.addEventListener('clusterstart', event => { Ti.API.info('clustering started!'); - var clusterAnnotation = Map.createAnnotation({ + const clusterAnnotation = Map.createAnnotation({ showAsMarker: true, - markerText: e.memberAnnotations.length.toString(), + markerText: event.memberAnnotations.length.toString(), title: 'Cluster Title', subtitle: 'Cluster Subtitle', }); - mapview.setClusterAnnotation({ + mapView.setClusterAnnotation({ annotation: clusterAnnotation, - memberAnnotations: e.memberAnnotations + memberAnnotations: event.memberAnnotations }); }); - win.add(mapview); - win.open(); + window.add(mapView); + window.open(); + ``` + - title: Search Request (iOS only) + example: | + The following example shows the MapKit based search request: + + ```javascript + import Map from 'ti.map'; + + Map.addEventListener('didUpdateResults', event => { + console.warn('Found place:'); + console.warn(event) + }); + + Map.search('Colloseum Rome'); ``` --- @@ -709,7 +765,27 @@ properties: - name: longitude summary: Longitude value of the map point, in decimal degrees. type: Number - - name: latitude summary: Latitude value of the map point, in decimal degrees. type: Number + +--- +name: SearchCompletionOptions +summary: Additional options to fine-tune the search request. +description: The latitute and longitude describe the location, the delta values the distance to include. +properties: + - name: latitude + summary: Latitude value of the search request, in decimal degrees. + type: Number + - name: longitude + summary: Longitude value of the search request, in decimal degrees. + type: Number + - name: latitude + summary: Latitude value of the search request, in decimal degrees. + type: Number + - name: latitudeDelta + summary: The amount of north-to-south distance displayed on the map, measured in decimal degrees. + type: Number + - name: longitudeDelta + summary: The amount of east-to-west distance displayed on the map, measured in decimal degrees. + type: Number \ No newline at end of file diff --git a/ios/Classes/TiMapModule.h b/ios/Classes/TiMapModule.h index 5eae48bd..f5bbc629 100644 --- a/ios/Classes/TiMapModule.h +++ b/ios/Classes/TiMapModule.h @@ -9,11 +9,12 @@ #import #if IS_SDK_IOS_16 -@interface TiMapModule : TiModule { +@interface TiMapModule : TiModule { #else @interface TiMapModule : TiModule { #endif UIColor *colorRed; + MKLocalSearchCompleter *_searchCompleter; } @property (nonatomic, readonly) NSNumber *STANDARD_TYPE; diff --git a/ios/Classes/TiMapModule.m b/ios/Classes/TiMapModule.m index dd006ede..4ae3b461 100644 --- a/ios/Classes/TiMapModule.m +++ b/ios/Classes/TiMapModule.m @@ -116,6 +116,123 @@ - (void)lookAroundViewControllerDidPresentFullScreen:(MKLookAroundViewController #endif +- (MKLocalSearchCompleter *)searchCompleter +{ + if (_searchCompleter == nil) { + _searchCompleter = [[MKLocalSearchCompleter alloc] init]; + _searchCompleter.delegate = self; + + // Do not show queries like "show my location" or "show nearby items" + if ([TiUtils isIOSVersionOrGreater:@"13.0"]) { + if (@available(iOS 13.0, *)) { + _searchCompleter.resultTypes = MKLocalSearchCompleterResultTypeAddress | MKLocalSearchCompleterResultTypePointOfInterest; + } else {} + } else { + _searchCompleter.filterType = MKSearchCompletionFilterTypeLocationsOnly; + } + } + + return _searchCompleter; +} + +- (void)search:(id)args +{ + ENSURE_UI_THREAD(search, args); + + NSString *value = [TiUtils stringValue:args[0]]; + NSDictionary *options = (NSDictionary *)args[1]; + + // 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 (options != nil) { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([TiUtils doubleValue:options[@"latitude"]], [TiUtils doubleValue:options[@"longitude"]]); + MKCoordinateSpan span = MKCoordinateSpanMake([TiUtils doubleValue:options[@"latitudeDelta"]], [TiUtils doubleValue:options[@"longitudeDelta"]]); + + if (CLLocationCoordinate2DIsValid(coordinate)) { + [[self searchCompleter] setRegion:MKCoordinateRegionMake(coordinate, span)]; + } + } + + [[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]), + @"postalCode": NULL_IF_NIL(place.postalCode), + @"city": NULL_IF_NIL(place.locality), + @"country": NULL_IF_NIL(place.country), + @"state": NULL_IF_NIL(place.administrativeArea), + @"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); diff --git a/ios/Classes/TiMapModuleAssets.m b/ios/Classes/TiMapModuleAssets.m index 1f348e39..a169d2dd 100644 --- a/ios/Classes/TiMapModuleAssets.m +++ b/ios/Classes/TiMapModuleAssets.m @@ -3,18 +3,20 @@ */ #import "TiMapModuleAssets.h" -extern NSData *filterDataInRange(NSData *thedata, NSRange range); +extern NSData* filterDataInRange(NSData* thedata, NSRange range); @implementation TiMapModuleAssets - (NSData *)moduleAsset { + return nil; } - (NSData *)resolveModuleAsset:(NSString *)path { + return nil; } diff --git a/ios/manifest b/ios/manifest index 4bdd1a61..6f0a1dcd 100644 --- a/ios/manifest +++ b/ios/manifest @@ -3,7 +3,7 @@ # during compilation, packaging, distribution, etc. # -version: 7.2.0 +version: 7.3.0 apiversion: 2 architectures: arm64 x86_64 description: External version of Map module From 57f1bee85a205f6bd73051504f1664369308c623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Fri, 22 Sep 2023 21:35:22 +0200 Subject: [PATCH 2/5] fix: fix linting error --- ios/Classes/TiMapModule.m | 81 ++++++++++++++++++--------------- ios/Classes/TiMapModuleAssets.m | 4 +- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/ios/Classes/TiMapModule.m b/ios/Classes/TiMapModule.m index 4ae3b461..092e7b97 100644 --- a/ios/Classes/TiMapModule.m +++ b/ios/Classes/TiMapModule.m @@ -126,12 +126,12 @@ - (MKLocalSearchCompleter *)searchCompleter if ([TiUtils isIOSVersionOrGreater:@"13.0"]) { if (@available(iOS 13.0, *)) { _searchCompleter.resultTypes = MKLocalSearchCompleterResultTypeAddress | MKLocalSearchCompleterResultTypePointOfInterest; - } else {} + } } else { _searchCompleter.filterType = MKSearchCompletionFilterTypeLocationsOnly; } } - + return _searchCompleter; } @@ -152,7 +152,7 @@ - (void)search:(id)args if (options != nil) { CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([TiUtils doubleValue:options[@"latitude"]], [TiUtils doubleValue:options[@"longitude"]]); MKCoordinateSpan span = MKCoordinateSpanMake([TiUtils doubleValue:options[@"latitudeDelta"]], [TiUtils doubleValue:options[@"longitudeDelta"]]); - + if (CLLocationCoordinate2DIsValid(coordinate)) { [[self searchCompleter] setRegion:MKCoordinateRegionMake(coordinate, span)]; } @@ -167,27 +167,32 @@ - (void)geocodeAddress:(id)args 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]), - @"postalCode": NULL_IF_NIL(place.postalCode), - @"city": NULL_IF_NIL(place.locality), - @"country": NULL_IF_NIL(place.country), - @"state": NULL_IF_NIL(place.administrativeArea), - @"latitude": @(place.location.coordinate.latitude), - @"longitude": @(place.location.coordinate.longitude), - }; - - [callback call:@[@{ @"success": @(YES), @"place": proxyPlace }] thisObject:self]; - }]; + [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]), + @"postalCode" : NULL_IF_NIL(place.postalCode), + @"city" : NULL_IF_NIL(place.locality), + @"country" : NULL_IF_NIL(place.country), + @"state" : NULL_IF_NIL(place.administrativeArea), + @"latitude" : @(place.location.coordinate.latitude), + @"longitude" : @(place.location.coordinate.longitude), + }; + + [callback call:@[ @{@"success" : @(YES), + @"place" : proxyPlace} ] + thisObject:self]; + }]; } - (NSString *)formattedStreetNameFromPlace:(CLPlacemark *)place @@ -203,34 +208,36 @@ - (NSString *)formattedStreetNameFromPlace:(CLPlacemark *)place - (void)completer:(MKLocalSearchCompleter *)completer didFailWithError:(NSError *)error { - [self fireEvent:@"didUpdateResults" withObject:@{ @"results": @[], @"error": error.localizedDescription }]; + [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) { + + [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.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) }]; + [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 + @"title" : obj.title, + @"subtitle" : obj.subtitle, + @"titleHighlightRanges" : titleHighlightRanges, + @"subtitleHighlightRanges" : subtitleHighlightRanges }]; }]; - [self fireEvent:@"didUpdateResults" withObject:@{ @"results": proxyResults }]; + [self fireEvent:@"didUpdateResults" withObject:@{ @"results" : proxyResults }]; } MAKE_SYSTEM_PROP(STANDARD_TYPE, MKMapTypeStandard); diff --git a/ios/Classes/TiMapModuleAssets.m b/ios/Classes/TiMapModuleAssets.m index a169d2dd..1f348e39 100644 --- a/ios/Classes/TiMapModuleAssets.m +++ b/ios/Classes/TiMapModuleAssets.m @@ -3,20 +3,18 @@ */ #import "TiMapModuleAssets.h" -extern NSData* filterDataInRange(NSData* thedata, NSRange range); +extern NSData *filterDataInRange(NSData *thedata, NSRange range); @implementation TiMapModuleAssets - (NSData *)moduleAsset { - return nil; } - (NSData *)resolveModuleAsset:(NSString *)path { - return nil; } From 9540f3ab720520d4af28a26662da2a736a082a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Sat, 23 Sep 2023 14:31:54 +0200 Subject: [PATCH 3/5] =?UTF-8?q?chore:=20expose=20=E2=80=9CresultTypes?= =?UTF-8?q?=E2=80=9D=20API,=20fix=20optional=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Classes/TiMapModule.m | 42 ++++++++++++++++++++----------- ios/Classes/TiMapUtils.h | 3 +++ ios/Classes/TiMapUtils.m | 12 +++++++++ ios/Classes/TiMapViewProxy.h | 1 - ios/map.xcodeproj/project.pbxproj | 4 +-- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/ios/Classes/TiMapModule.m b/ios/Classes/TiMapModule.m index 092e7b97..8b403a83 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 @@ -121,15 +122,6 @@ - (MKLocalSearchCompleter *)searchCompleter if (_searchCompleter == nil) { _searchCompleter = [[MKLocalSearchCompleter alloc] init]; _searchCompleter.delegate = self; - - // Do not show queries like "show my location" or "show nearby items" - if ([TiUtils isIOSVersionOrGreater:@"13.0"]) { - if (@available(iOS 13.0, *)) { - _searchCompleter.resultTypes = MKLocalSearchCompleterResultTypeAddress | MKLocalSearchCompleterResultTypePointOfInterest; - } - } else { - _searchCompleter.filterType = MKSearchCompletionFilterTypeLocationsOnly; - } } return _searchCompleter; @@ -140,7 +132,6 @@ - (void)search:(id)args ENSURE_UI_THREAD(search, args); NSString *value = [TiUtils stringValue:args[0]]; - NSDictionary *options = (NSDictionary *)args[1]; // Require a search value if (!value) { @@ -149,12 +140,29 @@ - (void)search:(id)args } // Pass additional options like search region - if (options != nil) { - CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([TiUtils doubleValue:options[@"latitude"]], [TiUtils doubleValue:options[@"longitude"]]); - MKCoordinateSpan span = MKCoordinateSpanMake([TiUtils doubleValue:options[@"latitudeDelta"]], [TiUtils doubleValue:options[@"longitudeDelta"]]); + 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[@"latitude"] && options[@"longitude"] && options[@"latitudeDelta"] && options[@"longitudeDelta"]) { + CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([TiUtils doubleValue:options[@"latitude"]], [TiUtils doubleValue:options[@"longitude"]]); + MKCoordinateSpan span = MKCoordinateSpanMake([TiUtils doubleValue:options[@"latitudeDelta"]], [TiUtils doubleValue:options[@"longitudeDelta"]]); - if (CLLocationCoordinate2DIsValid(coordinate)) { - [[self searchCompleter] setRegion:MKCoordinateRegionMake(coordinate, span)]; + 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+"); + } } } @@ -288,4 +296,8 @@ - (void)completerDidUpdateResults:(MKLocalSearchCompleter *)completer MAKE_SYSTEM_PROP(FEATURE_TYPE_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 diff --git a/ios/Classes/TiMapUtils.h b/ios/Classes/TiMapUtils.h index 4440cbd3..ece8353c 100644 --- a/ios/Classes/TiMapUtils.h +++ b/ios/Classes/TiMapUtils.h @@ -7,6 +7,7 @@ #import #import +#import @interface TiMapUtils : NSObject @@ -14,4 +15,6 @@ + (NSDictionary *)dictionaryFromPlacemark:(CLPlacemark *)placemark; ++ (MKLocalSearchCompleterResultType)mappedResultTypes:(NSArray *)inputResultTypes; + @end diff --git a/ios/Classes/TiMapUtils.m b/ios/Classes/TiMapUtils.m index 8ae44b37..85aec6fc 100644 --- a/ios/Classes/TiMapUtils.m +++ b/ios/Classes/TiMapUtils.m @@ -56,4 +56,16 @@ + (id)returnValueOnMainThread:(id (^)(void))block return place; } ++ (MKLocalSearchCompleterResultType)mappedResultTypes:(NSArray *)inputResultTypes +{ + MKLocalSearchCompleterResultType resultTypes = 0; + + for (NSNumber *number in inputResultTypes) { + MKLocalSearchCompleterResultType typeValue = [number unsignedIntegerValue]; + resultTypes |= typeValue; + } + + return resultTypes; +} + @end diff --git a/ios/Classes/TiMapViewProxy.h b/ios/Classes/TiMapViewProxy.h index a479d522..0eec9e8d 100644 --- a/ios/Classes/TiMapViewProxy.h +++ b/ios/Classes/TiMapViewProxy.h @@ -65,7 +65,6 @@ - (void)removeImageOverlay:(id)arg; - (void)removeAllImageOverlays:(id)args; - (void)setClusterAnnotation:(id)args; -- (void)setLocation:(id)location; - (NSNumber *)containsCoordinate:(id)args; @end diff --git a/ios/map.xcodeproj/project.pbxproj b/ios/map.xcodeproj/project.pbxproj index 2fd7b6d7..a6d15353 100644 --- a/ios/map.xcodeproj/project.pbxproj +++ b/ios/map.xcodeproj/project.pbxproj @@ -183,6 +183,8 @@ DB3656EF1E50BA740040E0B9 /* TiMapConstants.h */, 24DD6CF71134B3F500162E58 /* TiMapModule.h */, 24DD6CF81134B3F500162E58 /* TiMapModule.m */, + CEE33CF119EC4C150005E745 /* TiMapUtils.h */, + CEE33CF219EC4C150005E745 /* TiMapUtils.m */, 24DE9E0F11C5FE74003F90F6 /* TiMapModuleAssets.h */, 24DE9E1011C5FE74003F90F6 /* TiMapModuleAssets.m */, ); @@ -293,8 +295,6 @@ CED651C317D7AA21007C2954 /* TiMapView.m */, CED651C417D7AA21007C2954 /* TiMapViewProxy.h */, CED651C517D7AA21007C2954 /* TiMapViewProxy.m */, - CEE33CF119EC4C150005E745 /* TiMapUtils.h */, - CEE33CF219EC4C150005E745 /* TiMapUtils.m */, 27A609E819F5922B00CA150A /* WildcardGestureRecognizer.h */, 27A609E919F5927000CA150A /* WildcardGestureRecognizer.m */, ); From 0635f87c189d03dc72f8c15f1126f75f053f7faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Sat, 23 Sep 2023 14:39:50 +0200 Subject: [PATCH 4/5] chore: add new docs --- apidoc/Map.yml | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/apidoc/Map.yml b/apidoc/Map.yml index d26e601d..9f80383a 100644 --- a/apidoc/Map.yml +++ b/apidoc/Map.yml @@ -484,6 +484,27 @@ properties: osver: {ios: {min: "11.0"} } exclude-platforms: [android] since: "6.3.0" + + - name: SEARCH_RESULT_TYPE_ADDRESS + summary: A value that indicates that search results include addresses. + type: Number + permission: read-only + osver: {ios: {min: "13.0"} } + since: "12.3.0" + + - name: SEARCH_RESULT_TYPE_POINT_OF_INTEREST + summary: A value that indicates that search results include points of interest. + type: Number + permission: read-only + osver: {ios: {min: "13.0"} } + since: "12.3.0" + + - name: SEARCH_RESULT_TYPE_QUERY + summary: A value that indicates that the search completer includes query completions in results. + type: Number + permission: read-only + osver: {ios: {min: "13.0"} } + since: "12.3.0" methods: - name: isGooglePlayServicesAvailable @@ -788,4 +809,12 @@ properties: type: Number - name: longitudeDelta summary: The amount of east-to-west distance displayed on the map, measured in decimal degrees. - type: Number \ No newline at end of file + type: Number + - name: resultTypes + summary: These options configure the types of search results you want to receive, including points of interest and addresses. + description: | + Use one or more of the following constants: + - + - + - + type: Array \ No newline at end of file From 27e977ffdd9bccef24ac15ff184c9818ccb5fc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Sat, 23 Sep 2023 15:23:02 +0200 Subject: [PATCH 5/5] chore: move region to own key --- apidoc/Map.yml | 34 +++++++++++++++++----------------- ios/Classes/TiMapModule.m | 7 ++++--- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/apidoc/Map.yml b/apidoc/Map.yml index 9f80383a..3b980deb 100644 --- a/apidoc/Map.yml +++ b/apidoc/Map.yml @@ -766,7 +766,9 @@ examples: ``` - title: Search Request (iOS only) example: | - The following example shows the MapKit based search request: + The following example shows the MapKit based search request. + The options in `search` (2nd parameter) are optional, but improve + the accuracy of the results. ```javascript import Map from 'ti.map'; @@ -776,7 +778,15 @@ examples: console.warn(event) }); - Map.search('Colloseum Rome'); + Map.search('Colosseum', { + region: { + latitude: 41.890560, + longitude: 12.494270, + latitudeDelta: 1, + longitudeDelta: 1, + }, + resultTypes: [Map.SEARCH_RESULT_TYPE_POINT_OF_INTEREST, Map.SEARCH_RESULT_TYPE_ADDRESS] + }); ``` --- @@ -795,21 +805,11 @@ name: SearchCompletionOptions summary: Additional options to fine-tune the search request. description: The latitute and longitude describe the location, the delta values the distance to include. properties: - - name: latitude - summary: Latitude value of the search request, in decimal degrees. - type: Number - - name: longitude - summary: Longitude value of the search request, in decimal degrees. - type: Number - - name: latitude - summary: Latitude value of the search request, in decimal degrees. - type: Number - - name: latitudeDelta - summary: The amount of north-to-south distance displayed on the map, measured in decimal degrees. - type: Number - - name: longitudeDelta - summary: The amount of east-to-west distance displayed on the map, measured in decimal degrees. - type: Number + - name: region + summary: | + The region to look for results. Note that only `latitude`, `longitude`, `latitudeDelta` and `longitudeDelta` + are currently handled to define the region. + type: MapRegionTypev2 - name: resultTypes summary: These options configure the types of search results you want to receive, including points of interest and addresses. description: | diff --git a/ios/Classes/TiMapModule.m b/ios/Classes/TiMapModule.m index 8b403a83..d39b3c3b 100644 --- a/ios/Classes/TiMapModule.m +++ b/ios/Classes/TiMapModule.m @@ -147,9 +147,10 @@ - (void)search:(id)args } // Handle search region - if (options[@"latitude"] && options[@"longitude"] && options[@"latitudeDelta"] && options[@"longitudeDelta"]) { - CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake([TiUtils doubleValue:options[@"latitude"]], [TiUtils doubleValue:options[@"longitude"]]); - MKCoordinateSpan span = MKCoordinateSpanMake([TiUtils doubleValue:options[@"latitudeDelta"]], [TiUtils doubleValue:options[@"longitudeDelta"]]); + 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)];