Skip to content

Commit

Permalink
refactor(ios): use native APIs for reverse and forward geolocation (#…
Browse files Browse the repository at this point in the history
…12651)

Fixes TIMOB-28396
  • Loading branch information
vijaysingh-axway committed Apr 5, 2021
1 parent fdfe4fc commit f59f5b8
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 111 deletions.
7 changes: 7 additions & 0 deletions apidoc/Titanium/Geolocation/Geolocation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,13 @@ properties:
summary: Postal code
type: String
platforms: [android, iphone, ipad, macos]

- name: state
summary: State name.
description: |
This property may return the full name or abbreviated name.
type: String
platforms: [android, iphone, ipad, macos]

- name: zipcode
summary: Postal code. To be replaced by `postalCode`
Expand Down
183 changes: 73 additions & 110 deletions iphone/Classes/GeolocationModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
#ifdef USE_TI_GEOLOCATION

#import "GeolocationModule.h"
#import <Contacts/CNPostalAddress.h>
#import <Contacts/CNPostalAddressFormatter.h>
#import <TitaniumKit/APSHTTPClient.h>
#import <TitaniumKit/NSData+Additions.h>
#import <TitaniumKit/TiApp.h>

#import <sys/utsname.h>

extern NSString *const TI_APPLICATION_GUID;
Expand Down Expand Up @@ -39,35 +40,7 @@ - (void)dealloc
[super dealloc];
}

- (void)start:(NSDictionary *)params
{
// https://api.appcelerator.net/p/v1/geo
NSString *kGeolocationURL = stringWithHexString(@"68747470733a2f2f6170692e61707063656c657261746f722e636f6d2f702f76312f67656f");

NSMutableString *url = [[[NSMutableString alloc] init] autorelease];
[url appendString:kGeolocationURL];
[url appendString:@"?"];
for (id key in params) {
NSString *value = [TiUtils stringValue:[params objectForKey:key]];
[url appendFormat:@"%@=%@&", key, [value stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]]];
}

APSHTTPRequest *req = [[APSHTTPRequest alloc] init];
[req addRequestHeader:@"User-Agent" value:[[TiApp app] systemUserAgent]];
[req setUrl:[NSURL URLWithString:url]];
[req setDelegate:self];
[req setMethod:@"GET"];
// Place it in the main thread since we're not using a queue and yet we need the
// delegate methods to be called...
TiThreadPerformOnMainThread(
^{
[req send];
[req autorelease];
},
NO);
}

- (void)requestSuccess:(NSString *)data
- (void)requestSuccess:(NSDictionary *)data
{
}

Expand All @@ -77,27 +50,6 @@ - (void)requestError:(NSError *)error
[callback callWithArguments:@[ event ]];
}

- (void)request:(APSHTTPRequest *)request onLoad:(APSHTTPResponse *)response
{
[[TiApp app] stopNetwork];

if (request != nil && [response error] == nil) {
NSString *data = [response responseString];
[self requestSuccess:data];
} else {
[self requestError:[response error]];
}

[self autorelease];
}

- (void)request:(APSHTTPRequest *)request onError:(APSHTTPResponse *)response
{
[[TiApp app] stopNetwork];
[self requestError:[response error]];
[self autorelease];
}

@end

@interface ForwardGeoCallback : GeolocationCallback
Expand All @@ -108,49 +60,21 @@ @interface ReverseGeoCallback : GeolocationCallback

@implementation ForwardGeoCallback

- (void)requestSuccess:(NSString *)locationString
- (void)requestSuccess:(NSDictionary *)event
{
NSMutableDictionary *event = nil;

NSArray *listItems = [locationString componentsSeparatedByString:@","];
if ([listItems count] == 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"]) {
id accuracy = [listItems objectAtIndex:1];
id latitude = [listItems objectAtIndex:2];
id longitude = [listItems objectAtIndex:3];
event = [TiUtils dictionaryWithCode:0 message:nil];
[event setObject:accuracy forKey:@"accuracy"];
[event setObject:latitude forKey:@"latitude"];
[event setObject:longitude forKey:@"longitude"];
} else {
//TODO: better error handling
event = [TiUtils dictionaryWithCode:-1 message:@"error obtaining geolocation"];
if (callback != nil) {
[callback callWithArguments:@[ event ]];
}

[callback callWithArguments:@[ event ]];
}

@end

@implementation ReverseGeoCallback

- (void)requestSuccess:(NSString *)locationString
- (void)requestSuccess:(NSDictionary *)event
{
NSError *error = nil;
id event = [TiUtils jsonParse:locationString error:&error];
if (error != nil) {
[self requestError:error];
} else {
BOOL success = [TiUtils boolValue:@"success" properties:event def:YES];
NSMutableDictionary *revisedEvent = [TiUtils dictionaryWithCode:success ? 0 : -1 message:success ? nil : @"error reverse geocoding"];
[revisedEvent setValuesForKeysWithDictionary:event];
NSArray<NSMutableDictionary *> *places = (NSArray<NSMutableDictionary *> *)revisedEvent[@"places"];
for (NSMutableDictionary *dict in places) {
dict[@"postalCode"] = dict[@"zipcode"];
[dict removeObjectForKey:@"zipcode"];
dict[@"countryCode"] = dict[@"country_code"];
[dict removeObjectForKey:@"country_code"];
}
[callback callWithArguments:@[ revisedEvent ]];
if (callback != nil) {
[callback callWithArguments:@[ event ]];
}
}

Expand Down Expand Up @@ -472,42 +396,81 @@ - (BOOL)hasCompass

GETTER_IMPL(BOOL, hasCompass, HasCompass);

- (void)performGeo:(NSString *)direction address:(NSString *)address callback:(GeolocationCallback *)callback
{
[[TiApp app] startNetwork];

id aguid = TI_APPLICATION_GUID;
id sid = [[TiApp app] sessionId];

NSMutableDictionary *params = [@{
@"d" : direction,
@"aguid" : aguid,
@"mid" : [TiUtils appIdentifier],
@"sid" : sid,
@"q" : address,
} mutableCopy];

NSString *countryCode = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
if (countryCode) {
[params setValue:countryCode forKey:@"c"];
}
[callback start:params];
RELEASE_TO_NIL(params);
}

- (void)reverseGeocoder:(double)latitude longitude:(double)longitude withCallback:(JSValue *)callback
{
#ifndef __clang_analyzer__ // Ignore static analyzer error here, memory will be released. See TIMOB-19444
ReverseGeoCallback *rcb = [[ReverseGeoCallback alloc] initWithCallback:callback];
[self performGeo:@"r" address:[NSString stringWithFormat:@"%f,%f", latitude, longitude] callback:rcb];

CLGeocoder *geoCoder = [[[CLGeocoder alloc] init] autorelease];
CLLocation *clLocation = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude];
[geoCoder reverseGeocodeLocation:clLocation
preferredLocale:[NSLocale currentLocale]
completionHandler:^(NSArray<CLPlacemark *> *_Nullable placemarks, NSError *_Nullable error) {
if (error != nil) {
[rcb requestError:error];
} else {
NSMutableDictionary *events = [TiUtils dictionaryWithCode:0 message:nil];

NSMutableArray<NSDictionary *> *places = [NSMutableArray array];
for (CLPlacemark *placemark in placemarks) {
NSMutableDictionary *place = [NSMutableDictionary dictionary];

if (placemark.thoroughfare) {
place[@"street"] = placemark.thoroughfare;
}
if (placemark.locality) {
place[@"city"] = placemark.locality;
}
if (placemark.administrativeArea) {
place[@"state"] = placemark.administrativeArea;
}
if (placemark.country) {
place[@"country"] = placemark.country;
}
if (placemark.postalCode) {
place[@"postalCode"] = placemark.postalCode;
}
if (placemark.ISOcountryCode) {
place[@"countryCode"] = placemark.ISOcountryCode;
}
if (placemark.location) {
place[@"latitude"] = @(placemark.location.coordinate.latitude);
place[@"longitude"] = @(placemark.location.coordinate.longitude);
}
if (placemark.postalAddress) {
place[@"address"] = [CNPostalAddressFormatter stringFromPostalAddress:placemark.postalAddress style:CNPostalAddressFormatterStyleMailingAddress];
}
[places addObject:place];
}
events[@"places"] = places;
[rcb requestSuccess:events];
}
}];
#endif
}

- (void)forwardGeocoder:(NSString *)address withCallback:(JSValue *)callback
{
#ifndef __clang_analyzer__ // Ignore static analyzer error here, memory will be released. See TIMOB-19444
ForwardGeoCallback *fcb = [[ForwardGeoCallback alloc] initWithCallback:callback];
[self performGeo:@"f" address:address callback:fcb];

CLGeocoder *geoCoder = [[[CLGeocoder alloc] init] autorelease];
[geoCoder geocodeAddressString:address
inRegion:nil
preferredLocale:[NSLocale currentLocale]
completionHandler:^(NSArray<CLPlacemark *> *_Nullable placemarks, NSError *_Nullable error) {
if (error != nil) {
[fcb requestError:error];
} else {
NSMutableDictionary *events = [TiUtils dictionaryWithCode:0 message:nil];
CLPlacemark *placemark = [placemarks firstObject]; // For forward geocode, take first object only
if (placemark.location) {
events[@"latitude"] = @(placemark.location.coordinate.latitude);
events[@"longitude"] = @(placemark.location.coordinate.longitude);
}
[fcb requestSuccess:events];
}
}];
#endif
}

Expand Down
2 changes: 1 addition & 1 deletion tests/Resources/ti.geolocation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ describe.windowsBroken('Titanium.Geolocation', function () {
should(data.places[0]).have.property('latitude').which.is.a.Number();
should(data.places[0]).have.property('longitude').which.is.a.Number();
should(data.places[0].country).be.oneOf('USA', 'United States of America', 'United States');
should(data.places[0].state).be.eql('California');
should(data.places[0].state).be.oneOf('California', 'CA');
should(data.places[0].countryCode).be.eql('US');
should(data.places[0]).have.property('city').which.is.a.String();
should(data.places[0]).have.property('address').which.is.a.String();
Expand Down

0 comments on commit f59f5b8

Please sign in to comment.