Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for place photos and streetview photos #11

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FTGooglePlacesAPI.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ Pod::Spec.new do |s|
:tag => s.version.to_s }
s.source_files = 'FTGooglePlacesAPI/*.{h,m}'
s.requires_arc = true
s.dependency 'AFNetworking', '~> 2.2'
s.dependency 'AFNetworking'

end
1 change: 1 addition & 0 deletions FTGooglePlacesAPI/FTGooglePlacesAPISearchResultItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ typedef NS_ENUM(NSUInteger, FTGooglePlacesAPISearchResultItemOpenedState) {
@property (nonatomic, assign, readonly) CGFloat rating;
@property (nonatomic, strong, readonly) NSString *reference;
@property (nonatomic, strong, readonly) NSArray *types;
@property (nonatomic, strong, readonly) NSArray *photos;

/**
* You can access complete response dictionary using this property.
Expand Down
1 change: 1 addition & 0 deletions FTGooglePlacesAPI/FTGooglePlacesAPISearchResultItem.m
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ - (void)ftgpi_importDictionary:(NSDictionary *)dictionary
_reference = [dictionary ftgp_nilledObjectForKey:@"reference"];
_types = [dictionary ftgp_nilledObjectForKey:@"types"];

_photos = [dictionary ftgp_nilledObjectForKey:@"photos"];
// Deprecated, left for backwards compatibility
_itemId = [dictionary ftgp_nilledObjectForKey:@"id"];
}
Expand Down
45 changes: 45 additions & 0 deletions FTGooglePlacesAPI/FTGooglePlacesAPIService.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#import <Foundation/Foundation.h>

#import "FTGooglePlacesAPICommon.h"
#import "FTGooglePlacesAPISearchResultItem.h"

@class FTGooglePlacesAPISearchResponse;
@class FTGooglePlacesAPIDetailResponse;
Expand Down Expand Up @@ -68,6 +69,50 @@ typedef void (^FTGooglePlacesAPIDetailRequestCompletionhandler)(FTGooglePlacesAP
*/
+ (void)registerSearchResultItemClass:(Class)itemClass;

/**
* Return list of photo urls
*
* @param item search result item
*
* @return array of photo urls
*/
+ (NSArray *)photoURLsForPlace:(FTGooglePlacesAPISearchResultItem *)item;

/**
* List of photo urls for a given item
*
* @param item search result item
* @param maxWidth max width of photo
*
* @return array of photo urls
*/
+ (NSArray *)photoURLsForPlace:(FTGooglePlacesAPISearchResultItem *)item maxWidth:(NSInteger)maxWidth;


/**
* Returns the url for a given photo reference
*
* @param reference photo reference
* @param maxWidth maximiun width
*
* @return photo url
*/
+ (NSString *)photoURLForPhotoReference:(NSString *)reference maxWidth:(NSInteger)maxWidth;


/**
* Return photo url for streetview of location
*
* @param coordinate location coordinate
* @param maxWidth max width of photo
*
* @return string url of streetview photo
*/
+ (NSString *)streetViewPhotoURLForLocation:(CLLocationCoordinate2D)coordinate maxWidth:(NSInteger)maxWidth;

+ (NSString *)streetViewPhotoURLForLocation:(CLLocationCoordinate2D)coordinate;


/**
* Asks the service to execute the given Google Places API Places Search request.
*
Expand Down
194 changes: 117 additions & 77 deletions FTGooglePlacesAPI/FTGooglePlacesAPIService.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

#import "FTGooglePlacesAPIService.h"

#import "AFNetworking.h"
#import <AFNetworking/AFNetworking.h>
#import "FTGooglePlacesAPISearchResponse.h"
#import "FTGooglePlacesAPIDetailResponse.h"

Expand All @@ -41,14 +41,16 @@
* @param ... Output string parameters
*/
#ifdef DEBUG
#define FTGPServiceLog(format, ...) if (FTGooglePlacesAPIDebugLoggingEnabled) { NSLog(format, ##__VA_ARGS__); }
#define FTGPServiceLog(format, ...) if (FTGooglePlacesAPIDebugLoggingEnabled) { NSLog(format, ## __VA_ARGS__); }
#else
#define FTGPServiceLog(format, ...) ((void)0)
#define FTGPServiceLog(format, ...) ((void)0)
#endif


NSString *const FTGooglePlacesAPIBaseURL = @"https://maps.googleapis.com/maps/api/place/";

NSString *const FTGooglePlaceAPIPhotoURL = @"https://maps.googleapis.com/maps/api/place/photo?key=%@&maxwidth=%@&photoreference=%@";
NSString *const FTGooglePlaceStreetViewAPIURL = @"https://maps.googleapis.com/maps/api/streetview?key=%@&location=%@&size=%@";

static BOOL FTGooglePlacesAPIDebugLoggingEnabled;

Expand All @@ -64,7 +66,7 @@ @interface FTGooglePlacesAPIService ()
* AFNetworking request manager. This manager is lazily intitialized in custom getter.
* Default implementation is initialized with base URL of Google Places API
*/
@property (nonatomic, strong) AFHTTPRequestOperationManager *httpRequestOperationManager;
@property (nonatomic, strong) AFHTTPSessionManager *httpRequestOperationManager;

@property (nonatomic, copy) NSString *apiKey;
@property (nonatomic, weak) Class searchResultsItemClass;
Expand All @@ -82,7 +84,7 @@ + (FTGooglePlacesAPIService *)sharedService;
*/
@interface FTGooglePlacesAPIService (Private)

+ (NSError *)ftgp_errorForResponseStatus:(FTGooglePlacesAPIResponseStatus)status;
+ (NSError *) ftgp_errorForResponseStatus:(FTGooglePlacesAPIResponseStatus)status;

@end

Expand All @@ -99,6 +101,7 @@ + (FTGooglePlacesAPIService *)sharedService
static FTGooglePlacesAPIService *SharedInstance;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
SharedInstance = [[FTGooglePlacesAPIService alloc] init];
});
Expand All @@ -109,11 +112,13 @@ + (FTGooglePlacesAPIService *)sharedService
- (id)init
{
self = [super init];

if (self) {
_apiKey = nil;
_searchResultsItemClass = nil;
FTGooglePlacesAPIDebugLoggingEnabled = NO;
}

return self;
}

Expand All @@ -127,12 +132,11 @@ - (void)setApiKey:(NSString *)apiKey

#pragma mark Private

- (AFHTTPRequestOperationManager *)httpRequestOperationManager
- (AFHTTPSessionManager *)httpRequestOperationManager
{
if (!_httpRequestOperationManager)
{
if (!_httpRequestOperationManager) {
NSURL *baseUrl = [NSURL URLWithString:FTGooglePlacesAPIBaseURL];
_httpRequestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseUrl];
_httpRequestOperationManager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseUrl];
_httpRequestOperationManager.requestSerializer = [AFHTTPRequestSerializer serializer];
}

Expand All @@ -151,62 +155,103 @@ + (void)registerSearchResultItemClass:(Class)itemClass
[[[self class] sharedService] setSearchResultsItemClass:itemClass];
}

+ (void)executeSearchRequest:(id<FTGooglePlacesAPIRequest>)request
+ (NSArray *)photoURLsForPlace:(FTGooglePlacesAPISearchResultItem *)item maxWidth:(NSInteger)maxWidth
{
NSMutableArray *array = [NSMutableArray arrayWithCapacity:item.photos.count];

for (NSDictionary *photo in item.photos) {
NSInteger width = MIN(maxWidth, [[photo objectForKey:@"width"] integerValue]);
NSString *photoReference = [photo objectForKey:@"photo_reference"];
[array addObject:[self photoURLForPhotoReference:photoReference maxWidth:width]];
}

return array;
}

+ (NSArray *)photoURLsForPlace:(FTGooglePlacesAPISearchResultItem *)item
{
return [[self class] photoURLsForPlace:item maxWidth:10000];
}

+ (NSString *)photoURLForPhotoReference:(NSString *)reference maxWidth:(NSInteger)maxWidth
{
NSString *sizeS = maxWidth > 0 ? @(maxWidth).description : @"%@";

return [NSString stringWithFormat:FTGooglePlaceAPIPhotoURL, [self sharedService].apiKey, sizeS, reference];
}

+ (NSString *)streetViewPhotoURLForLocation:(CLLocationCoordinate2D)coordinate
{
return [self streetViewPhotoURLForLocation:coordinate maxWidth:1000];
}

+ (NSString *)streetViewPhotoURLForLocation:(CLLocationCoordinate2D)coordinate maxWidth:(NSInteger)maxWidth
{
NSString *locationString = [NSString stringWithFormat:@"%@,%@", @(coordinate.latitude), @(coordinate.longitude)];
NSString *sizeString = maxWidth > 0 ? [NSString stringWithFormat:@"%@x%@", @(maxWidth), @(maxWidth)] : @"%1$@x%1$@";

return [NSString stringWithFormat:FTGooglePlaceStreetViewAPIURL, [self sharedService].apiKey, locationString, sizeString];
}

+ (void)executeSearchRequest:(id <FTGooglePlacesAPIRequest> )request
withCompletionHandler:(FTGooglePlacesAPISearchRequestCompletionHandler)completion
{
[[self class] executeRequest:request withCompletionHandler:^(NSDictionary *responseObject, NSError *error) {

// Networing, parsing or other general error
if (error) {
completion(nil, error);
return;
}

// Parse response
Class resultsItemClass = [[[self class] sharedService] searchResultsItemClass];

FTGooglePlacesAPISearchResponse *response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:responseObject request:request resultsItemClass:resultsItemClass];

FTGPServiceLog(@"%@ received Search response. Status: %@, number of results: %ld", [self class], [FTGooglePlacesAPISearchResponse localizedNameOfStatus:response.status], (unsigned long)[response.results count]);

// Check if everything went OK
if (response && response.status == FTGooglePlacesAPIResponseStatusOK) {
completion(response, nil);
}
// If network request was successfull, but Google Places API
// responded with error status code
else {
completion(response, [[self class] ftgp_errorForResponseStatus:response.status]);
}
}];
[[self class] executeRequest:request
withCompletionHandler: ^(NSDictionary *responseObject, NSError *error) {
// Networing, parsing or other general error
if (error) {
completion(nil, error);
return;
}

// Parse response
Class resultsItemClass = [[[self class] sharedService] searchResultsItemClass];

FTGooglePlacesAPISearchResponse *response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:responseObject
request:request
resultsItemClass:resultsItemClass];

FTGPServiceLog(@"%@ received Search response. Status: %@, number of results: %ld", [self class], [FTGooglePlacesAPISearchResponse localizedNameOfStatus:response.status], (unsigned long)[response.results count]);

// Check if everything went OK
if (response && response.status == FTGooglePlacesAPIResponseStatusOK) {
completion(response, nil);
}
// If network request was successfull, but Google Places API
// responded with error status code
else {
completion(response, [[self class] ftgp_errorForResponseStatus:response.status]);
}
}];
}

+ (void)executeDetailRequest:(id<FTGooglePlacesAPIRequest>)request
+ (void)executeDetailRequest:(id <FTGooglePlacesAPIRequest> )request
withCompletionHandler:(FTGooglePlacesAPIDetailRequestCompletionhandler)completion
{
[[self class] executeRequest:request withCompletionHandler:^(NSDictionary *responseObject, NSError *error) {

// Networing, parsing or other general error
if (error) {
completion(nil, error);
return;
}

// Try to parse response to object
FTGooglePlacesAPIDetailResponse *response = [[FTGooglePlacesAPIDetailResponse alloc] initWithDictionary:responseObject request:request];

FTGPServiceLog(@"%@ received Detail response. Status: %@", [self class], [FTGooglePlacesAPISearchResponse localizedNameOfStatus:response.status]);

// Check if everything went OK
if (response && response.status == FTGooglePlacesAPIResponseStatusOK) {
completion(response, nil);
}
// If network request was successfull, but Google Places API
// responded with error status code
else {
completion(response, [[self class] ftgp_errorForResponseStatus:response.status]);
}
}];
[[self class] executeRequest:request
withCompletionHandler: ^(NSDictionary *responseObject, NSError *error) {
// Networing, parsing or other general error
if (error) {
completion(nil, error);
return;
}

// Try to parse response to object
FTGooglePlacesAPIDetailResponse *response = [[FTGooglePlacesAPIDetailResponse alloc] initWithDictionary:responseObject
request:request];

FTGPServiceLog(@"%@ received Detail response. Status: %@", [self class], [FTGooglePlacesAPISearchResponse localizedNameOfStatus:response.status]);

// Check if everything went OK
if (response && response.status == FTGooglePlacesAPIResponseStatusOK) {
completion(response, nil);
}
// If network request was successfull, but Google Places API
// responded with error status code
else {
completion(response, [[self class] ftgp_errorForResponseStatus:response.status]);
}
}];
}

+ (void)setDebugLoggingEnabled:(BOOL)enabled
Expand All @@ -216,8 +261,8 @@ + (void)setDebugLoggingEnabled:(BOOL)enabled

#pragma mark - Private class methods

+ (void)executeRequest:(id<FTGooglePlacesAPIRequest>)request
withCompletionHandler:(void(^)(NSDictionary *responseObject, NSError *error))completion
+ (void) executeRequest:(id <FTGooglePlacesAPIRequest> )request
withCompletionHandler:(void (^)(NSDictionary *responseObject, NSError *error))completion
{
NSAssert(completion, @"You must provide completion block for the Google Places API request execution. Performing request without handling does not make any sense.");

Expand All @@ -239,21 +284,16 @@ + (void)executeRequest:(id<FTGooglePlacesAPIRequest>)request
NSString *requestPath = [NSString stringWithFormat:@"%@/json", [request placesAPIRequestMethod]];

// Perform request using AFNetworking
AFHTTPRequestOperationManager *manager = service.httpRequestOperationManager;
AFHTTPSessionManager *manager = service.httpRequestOperationManager;

// Perform request
[manager GET:requestPath
parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
FTGPServiceLog(@"%@ request SUCCESS (Request URL: %@)", [self class], operation.request.URL);
completion(responseObject, nil);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
FTGPServiceLog(@"%@ request FAILURE: (Request URL: %@, Error: %@)", [self class], operation.request.URL, error);
completion(nil, error);
}];
[manager GET:requestPath parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
FTGPServiceLog(@"%@ request SUCCESS (Request URL: %@)", [self class], task.originalRequest.URL);
completion(responseObject, nil);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
FTGPServiceLog(@"%@ request FAILURE: (Request URL: %@, Error: %@)", [self class], task.originalRequest.URL, error);
completion(nil, error);
}];
}

@end
Expand All @@ -265,9 +305,9 @@ @implementation FTGooglePlacesAPIService (Private)
+ (NSError *)ftgp_errorForResponseStatus:(FTGooglePlacesAPIResponseStatus)status
{
NSDictionary *userInfo = @{
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Google Places API request failed", nil),
NSLocalizedDescriptionKey: [FTGooglePlacesAPIResponse localizedDescriptionForStatus:status]
};
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Google Places API request failed", nil),
NSLocalizedDescriptionKey: [FTGooglePlacesAPIResponse localizedDescriptionForStatus:status]
};

return [NSError errorWithDomain:FTGooglePlacesAPIErrorDomain code:status userInfo:userInfo];
}
Expand Down