├── .gitignore ├── .gitmodules ├── FTGooglePlacesAPI.podspec ├── FTGooglePlacesAPI ├── FTGooglePlacesAPI.h ├── FTGooglePlacesAPICommon.h ├── FTGooglePlacesAPICommon.m ├── FTGooglePlacesAPIDetailRequest.h ├── FTGooglePlacesAPIDetailRequest.m ├── FTGooglePlacesAPIDetailResponse.h ├── FTGooglePlacesAPIDetailResponse.m ├── FTGooglePlacesAPIDictionaryRequest.h ├── FTGooglePlacesAPIDictionaryRequest.m ├── FTGooglePlacesAPINearbySearchRequest.h ├── FTGooglePlacesAPINearbySearchRequest.m ├── FTGooglePlacesAPIResponse.h ├── FTGooglePlacesAPIResponse.m ├── FTGooglePlacesAPISearchResponse.h ├── FTGooglePlacesAPISearchResponse.m ├── FTGooglePlacesAPISearchResultItem.h ├── FTGooglePlacesAPISearchResultItem.m ├── FTGooglePlacesAPIService.h ├── FTGooglePlacesAPIService.m ├── FTGooglePlacesAPITextSearchRequest.h └── FTGooglePlacesAPITextSearchRequest.m ├── LICENSE ├── README.md └── XcodeProject ├── FTGooglePlacesAPI.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── Demo-FTGooglePlacesAPI.xcscheme │ └── FTGooglePlacesAPILogicTests.xcscheme ├── FTGooglePlacesAPI ├── FTAppDelegate.h ├── FTAppDelegate.m ├── FTGooglePlacesAPI-Info.plist ├── FTGooglePlacesAPI-Prefix.pch ├── FTGooglePlacesAPIExampleDetailViewController.h ├── FTGooglePlacesAPIExampleDetailViewController.m ├── FTGooglePlacesAPIExampleResultsViewController.h ├── FTGooglePlacesAPIExampleResultsViewController.m ├── FTGooglePlacesAPIExamplesListViewController.h ├── FTGooglePlacesAPIExamplesListViewController.m ├── Resources │ └── Images │ │ ├── Splashscreen │ │ ├── Default-568h@2x.png │ │ ├── Default.png │ │ └── Default@2x.png │ │ ├── powered-by-google-on-white.png │ │ └── powered-by-google-on-white@2x.png ├── en.lproj │ └── InfoPlist.strings └── main.m └── FTGooglePlacesAPILogicTests ├── FTGooglePlacesAPILogicTests-Info.plist ├── Source ├── FTGooglePlacesAPIDictionaryRequestTest.m ├── FTGooglePlacesAPINearbySearchRequestTests.m ├── FTGooglePlacesAPIResponseTests.m ├── FTGooglePlacesAPISearchResponseTests.m ├── FTGooglePlacesAPISearchResultItemTests.m ├── FTGooglePlacesAPIServiceTests.m ├── FTGooglePlacesAPITestCase.h ├── FTGooglePlacesAPITestCase.m └── Mocks │ ├── MockAFHTTPRequestOperationManager.h │ ├── MockAFHTTPRequestOperationManager.m │ ├── MockFTGooglePlacesAPISearchResultItemSubclass.h │ ├── MockFTGooglePlacesAPISearchResultItemSubclass.m │ ├── MockFTGooglePlacesAPIService.h │ └── MockFTGooglePlacesAPIService.m ├── TestData ├── FTGooglePlacesAPIResponse-test1-Nearby-Search-OK.json ├── FTGooglePlacesAPIResponse-test2-NoResults.json ├── FTGooglePlacesAPIResponse-test3-InvalidRequest.json └── FTGooglePlacesAPIResponse-test4-RequestDenied.json ├── Thirdparty └── OCMock │ ├── OCMock │ ├── NSNotificationCenter+OCMAdditions.h │ ├── OCMArg.h │ ├── OCMConstraint.h │ ├── OCMock.h │ ├── OCMockObject.h │ └── OCMockRecorder.h │ └── libOCMock.a └── en.lproj └── InfoPlist.strings /.gitignore: -------------------------------------------------------------------------------- 1 | XcodeProject/FTGooglePlacesAPI.xcodeproj/project.xcworkspace/xcuserdata 2 | XcodeProject/FTGooglePlacesAPI.xcodeproj/xcuserdata 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "XcodeProject/FTGooglePlacesAPI/Thirdparty/AFNetworking"] 2 | path = XcodeProject/FTGooglePlacesAPI/Thirdparty/AFNetworking 3 | url = git@github.com:AFNetworking/AFNetworking.git 4 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "FTGooglePlacesAPI" 4 | s.version = "1.1" 5 | s.summary = "iOS library for querying Google Places API using simple block-based interface" 6 | 7 | s.description = <<-DESC 8 | FTGooglePlacesAPI allows you to perform Google Places API requests with ease in a few lines of code. Library includes, but is not limited to, following: 9 | 10 | - Place Search 11 | - Nearby Search (search places withing a specified area) 12 | - Text Search (search places based on a search string) 13 | - Place Details (get more comprehensive information about a place) 14 | DESC 15 | 16 | s.homepage = "https://github.com/FuerteInternational/FTGooglePlacesAPI" 17 | s.screenshots = "http://i.imgur.com/DGMbcw1.png", "http://i.imgur.com/1POEIRN.png", "http://i.imgur.com/ATk5qhR.png" 18 | s.license = { :type => 'MIT', :file => 'LICENSE' } 19 | s.authors = { "Fuerte International" => "open-source@fuerteint.com", 20 | "Lukas Kukacka" => "opensource@lukaskukacka.com" } 21 | s.platform = :ios, '6.0' 22 | s.source = { :git => "https://github.com/FuerteInternational/FTGooglePlacesAPI.git", 23 | :tag => s.version.to_s } 24 | s.source_files = 'FTGooglePlacesAPI/*.{h,m}' 25 | s.requires_arc = true 26 | s.dependency 'AFNetworking', '~> 2.2' 27 | 28 | end 29 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPI.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPI.h 3 | // 4 | // Created by Lukas Kukacka on 10/29/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | 30 | #ifndef _FTGOOGLEPLACESAPI_ 31 | #define _FTGOOGLEPLACESAPI_ 32 | 33 | #import "FTGooglePlacesAPICommon.h" 34 | #import "FTGooglePlacesAPIService.h" 35 | #import "FTGooglePlacesAPIDictionaryRequest.h" 36 | #import "FTGooglePlacesAPINearbySearchRequest.h" 37 | #import "FTGooglePlacesAPITextSearchRequest.h" 38 | #import "FTGooglePlacesAPISearchResponse.h" 39 | #import "FTGooglePlacesAPISearchResultItem.h" 40 | #import "FTGooglePlacesAPIDetailRequest.h" 41 | #import "FTGooglePlacesAPIDetailResponse.h" 42 | 43 | #endif -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPICommon.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPICommon.h 3 | // 4 | // Created by Lukas Kukacka on 10/29/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import 30 | 31 | /** 32 | * Error domain used in NSErrors representing error response code returned 33 | * in a Places API response 34 | */ 35 | extern NSString * const FTGooglePlacesAPIErrorDomain; 36 | 37 | /** 38 | * Each request passed to the FTGooglePlacesAPIService needs to implement this 39 | * protocol. 40 | */ 41 | @protocol FTGooglePlacesAPIRequest 42 | 43 | /** 44 | * Returns type (method) of request as a string used in the request URL as a last component 45 | * of the Places API url (after ../place/ and before /json?params) 46 | * 47 | * @return String usable in the request URL 48 | */ 49 | - (NSString *)placesAPIRequestMethod; 50 | 51 | /** 52 | * Returns dictionary of all request parameters (without "output" format) 53 | * Keys are parameter names, values are values for a given name in a proper format. 54 | * For params which should be just defined, without value, set value to [NSNull null] 55 | * 56 | * This method should NOT contain following keys/values: 57 | * - API key as the service takes care of authentication 58 | * - "sensor" as the service takes care of this itself 59 | * 60 | * @return Dictionary of request parameters. 61 | */ 62 | - (NSDictionary *)placesAPIRequestParams; 63 | 64 | @end 65 | 66 | // Utilities 67 | 68 | @interface FTGooglePlacesAPIUtils : NSObject 69 | 70 | /** 71 | * Returns device language (localization) read from NSUserDefaults 72 | * 73 | * @return Device language or nil if cannot be determined 74 | */ 75 | + (NSString *)deviceLanguage; 76 | 77 | @end 78 | 79 | 80 | // Helper categories 81 | 82 | @interface NSDictionary (FTGPAdditions) 83 | 84 | /** 85 | * Returns object for a given key, ensuring [NSNull null] will never be returned 86 | * and "nil" will be returned instead. 87 | * This can greatly reduce risk of the crash on "[NSNull null] unrecognized selector" 88 | * when used for parsing JSON 89 | * 90 | * @param aKey Key of the object to return 91 | * 92 | * @return Object for a given key. If the object was [NSNull null], "nil" is returned instead 93 | */ 94 | - (id)ftgp_nilledObjectForKey:(id)aKey; 95 | 96 | /** 97 | * "valueForKeyPath" variant of - (id)ftgp_nilledObjectForKey:(id)aKey 98 | */ 99 | - (id)ftgp_nilledValueForKeyPath:(NSString *)keyPath; 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPICommon.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPICommon.m 3 | // 4 | // Created by Lukas Kukacka on 10/29/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import "FTGooglePlacesAPICommon.h" 30 | 31 | NSString * const FTGooglePlacesAPIErrorDomain = @"FTGooglePlacesAPIErrorDomain"; 32 | 33 | @implementation FTGooglePlacesAPIUtils 34 | 35 | + (NSString *)deviceLanguage 36 | { 37 | // Try to determine default language as a currently active language 38 | // from the NSUserDefaults 39 | // See https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/Articles/ChoosingLocalizations.html 40 | // Current language is cached so we have to always check agains NSUserDefaults 41 | // Language should never change while the app is running, so it should be ok 42 | static NSString *CurrentAppLanguage = nil; 43 | static BOOL AlreadyTriedToFindLanguage; 44 | 45 | if (!AlreadyTriedToFindLanguage && !CurrentAppLanguage) { 46 | NSArray* languages = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLanguages"]; 47 | if ([languages count] > 0) { 48 | CurrentAppLanguage = languages[0]; 49 | } 50 | AlreadyTriedToFindLanguage = YES; 51 | } 52 | 53 | return CurrentAppLanguage; 54 | } 55 | 56 | @end 57 | 58 | @implementation NSDictionary (FTGPAdditions) 59 | 60 | - (id)ftgp_nilledObjectForKey:(NSString *)key 61 | { 62 | id object = [self objectForKey:key]; 63 | return (object == [NSNull null] ? nil : object); 64 | } 65 | 66 | - (id)ftgp_nilledValueForKeyPath:(NSString *)keyPath 67 | { 68 | id value = [self valueForKeyPath:keyPath]; 69 | return (value == [NSNull null] ? nil : value); 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIDetailRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIDetailRequest.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 29/11/13. 6 | // 7 | // 8 | // The MIT License (MIT) 9 | // 10 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is 17 | // furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | // THE SOFTWARE. 29 | 30 | 31 | #import 32 | 33 | #import "FTGooglePlacesAPICommon.h" 34 | 35 | /** 36 | * Request on an item details 37 | */ 38 | @interface FTGooglePlacesAPIDetailRequest : NSObject 39 | 40 | @property (nonatomic, strong, readonly) NSString *placeId; 41 | 42 | /** 43 | * Creates new request on a detail of place with given placeId 44 | */ 45 | - (instancetype)initWithPlaceId:(NSString *)placeId; 46 | 47 | /** 48 | * Deprecations 49 | */ 50 | 51 | /** 52 | * Reference with which the request was initialized. 53 | * This is a unique identifier for a place (itemId from the ResultItem) 54 | */ 55 | @property (nonatomic, strong, readonly) NSString *reference DEPRECATED_MSG_ATTRIBUTE("Deprecated in API by Google as of June 24, 2014. Use placeId instead. See https://developers.google.com/places/documentation/details#deprecation for more info."); 56 | 57 | - (instancetype)initWithReference:(NSString *)reference DEPRECATED_MSG_ATTRIBUTE("Uses deprecated property reference (see @reference)"); 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIDetailRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIDetailRequest.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 29/11/13. 6 | // 7 | // 8 | // The MIT License (MIT) 9 | // 10 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is 17 | // furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | // THE SOFTWARE. 29 | 30 | 31 | #import "FTGooglePlacesAPIDetailRequest.h" 32 | 33 | @implementation FTGooglePlacesAPIDetailRequest 34 | 35 | #pragma mark Lifecycle 36 | 37 | - (instancetype)initWithPlaceId:(NSString *)placeId 38 | { 39 | if ([placeId length] == 0) { 40 | NSLog(@"WARNING: Trying to create FTGooglePlacesAPIDetailRequest with empty placeId. Returning nil"); 41 | return nil; 42 | } 43 | 44 | self = [super init]; 45 | if (self) { 46 | _placeId = placeId; 47 | } 48 | return self; 49 | } 50 | 51 | #pragma mark - Superclass overrides 52 | 53 | - (NSString *)description 54 | { 55 | return [NSString stringWithFormat:@"<%@: %p> Place ID: %@", [self class], self, _placeId]; 56 | } 57 | 58 | #pragma mark - FTGooglePlacesAPIRequest protocol 59 | 60 | - (NSString *)placesAPIRequestMethod 61 | { 62 | return @"details"; 63 | } 64 | 65 | - (NSDictionary *)placesAPIRequestParams 66 | { 67 | if (_placeId) { 68 | return @{@"placeid": _placeId}; 69 | } 70 | else { 71 | return @{@"reference": _reference}; 72 | } 73 | 74 | } 75 | 76 | #pragma mark - Deprecations 77 | 78 | - (instancetype)initWithReference:(NSString *)reference 79 | { 80 | if ([reference length] == 0) { 81 | NSLog(@"WARNING: Trying to create FTGooglePlacesAPIDetailRequest with empty reference. Returning nil"); 82 | return nil; 83 | } 84 | 85 | self = [super init]; 86 | if (self) { 87 | _reference = reference; 88 | } 89 | return self; 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIDetailResponse.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIDetailResponse.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 29/11/13. 6 | // 7 | // 8 | // The MIT License (MIT) 9 | // 10 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is 17 | // furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | // THE SOFTWARE. 29 | // 30 | // 31 | // Some of properties descriptions are copied from the Google Places API Documentation 32 | // at https://developers.google.com/places/documentation/details 33 | // Since this library aims to provide almost complete access to the places, 34 | // Google's docs descriptions are most relevant. 35 | 36 | 37 | #import "FTGooglePlacesAPIResponse.h" 38 | #import 39 | 40 | @interface FTGooglePlacesAPIDetailResponse : FTGooglePlacesAPIResponse 41 | 42 | /** 43 | * Contain a set of attributions about this listing which must be displayed to the user 44 | * to conform Google Terms and Conditions 45 | */ 46 | @property (nonatomic, strong, readonly) NSArray *htmlAttributions; 47 | 48 | /** 49 | * A unique stable identifier denoting this place. This identifier may not be 50 | * used to retrieve information about this place, but can be used to consolidate 51 | * data about this Place, and to verify the identity of a Place across separate 52 | * searches. As ids can occasionally change, it's recommended that the stored id 53 | * for a Place be compared with the id returned in later Details requests for 54 | * the same Place, and updated if necessary. 55 | */ 56 | @property (nonatomic, strong, readonly) NSString *itemId; 57 | 58 | /** 59 | * Human-readable name for the returned result. 60 | */ 61 | @property (nonatomic, strong, readonly) NSString *name; 62 | 63 | /** 64 | * CLLocation representing results coordinates. If the returned coordinates were invalid, 65 | * location will be nil 66 | */ 67 | @property (nonatomic, strong, readonly) CLLocation *location; 68 | 69 | /** 70 | * "vicinity" from response 71 | * Simplified address for the Place, including the street name, street number, 72 | * and locality, but not the province/state, postal code, or country. 73 | */ 74 | @property (nonatomic, strong, readonly) NSString *addressString; 75 | 76 | /** 77 | * String containing the human-readable address of this place. 78 | * Often this address is equivalent to the "postal address," which sometimes 79 | * differs from country to country. This address is generally composed of 80 | * one or more address_component fields 81 | */ 82 | @property (nonatomic, strong, readonly) NSString *formattedAddress; 83 | 84 | @property (nonatomic, strong, readonly) NSString *formattedPhoneNumber; 85 | @property (nonatomic, strong, readonly) NSString *internationalPhoneNumber; 86 | 87 | @property (nonatomic, strong, readonly) NSString *iconImageUrl; 88 | @property (nonatomic, assign, readonly) CGFloat rating; 89 | 90 | /** 91 | * Contains a unique token that you can use to retrieve additional information 92 | * about this place in a Place Details request. You can store this token and use 93 | * it at any time in future to refresh cached data about this Place, but the same 94 | * token is not guaranteed to be returned for any given Place across different searches. 95 | */ 96 | @property (nonatomic, strong, readonly) NSString *reference; 97 | @property (nonatomic, strong, readonly) NSArray *types; 98 | @property (nonatomic, strong, readonly) NSURL *url; 99 | @property (nonatomic, strong, readonly) NSURL *websiteUrl; 100 | 101 | @property (nonatomic, assign, readonly) NSTimeInterval utcOffset; 102 | 103 | @end 104 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIDetailResponse.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIDetailResponse.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 29/11/13. 6 | // 7 | // 8 | // The MIT License (MIT) 9 | // 10 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is 17 | // furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | // THE SOFTWARE. 29 | 30 | 31 | #import "FTGooglePlacesAPIDetailResponse.h" 32 | 33 | #import "FTGooglePlacesAPICommon.h" 34 | 35 | @interface FTGooglePlacesAPIDetailResponse (Private) 36 | 37 | - (void)ftgp_importDictionary:(NSDictionary *)dictionary; 38 | 39 | @end 40 | 41 | 42 | #pragma mark - 43 | 44 | @implementation FTGooglePlacesAPIDetailResponse 45 | 46 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary request:(id)request 47 | { 48 | self = [super initWithDictionary:dictionary request:request]; 49 | if (self) { 50 | [self ftgp_importDictionary:dictionary[@"result"]]; 51 | } 52 | return self; 53 | } 54 | 55 | @end 56 | 57 | 58 | @implementation FTGooglePlacesAPIDetailResponse (Private) 59 | 60 | // Private methods are prefixed to avoid methods names collisions on subclassing 61 | 62 | - (void)ftgp_importDictionary:(NSDictionary *)dictionary 63 | { 64 | _itemId = [dictionary ftgp_nilledObjectForKey:@"id"]; 65 | _name = [dictionary ftgp_nilledObjectForKey:@"name"]; 66 | 67 | 68 | // location is nil by default on errors or returned invalid values of Lat and Lon 69 | _location = nil; 70 | 71 | // Extract Lat and Lon from the response. If combination of Lat and Lon make 72 | // valid coordinates, create CLLocation object from them 73 | NSDictionary *geometryDict = [dictionary ftgp_nilledObjectForKey:@"geometry"]; 74 | NSNumber *latitudeNumber = [geometryDict ftgp_nilledValueForKeyPath:@"location.lat"]; 75 | NSNumber *longitudeNumber = [geometryDict ftgp_nilledValueForKeyPath:@"location.lng"]; 76 | if (geometryDict && latitudeNumber && longitudeNumber) 77 | { 78 | CLLocationDegrees latitude = [latitudeNumber doubleValue]; 79 | CLLocationDegrees longitude = [longitudeNumber doubleValue]; 80 | 81 | // If the LAT and LON are valid, create CLLocation object 82 | CLLocationCoordinate2D locationCoordinate = CLLocationCoordinate2DMake(latitude, longitude); 83 | 84 | if (CLLocationCoordinate2DIsValid(locationCoordinate)) { 85 | _location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude]; 86 | } 87 | 88 | } 89 | 90 | _addressString = [dictionary ftgp_nilledObjectForKey:@"vicinity"]; 91 | 92 | _formattedAddress = [dictionary ftgp_nilledObjectForKey:@"formatted_address"]; 93 | 94 | _formattedPhoneNumber = [dictionary ftgp_nilledObjectForKey:@"formatted_phone_number"]; 95 | _internationalPhoneNumber = [dictionary ftgp_nilledObjectForKey:@"international_phone_number"]; 96 | 97 | _iconImageUrl = [dictionary ftgp_nilledObjectForKey:@"icon"]; 98 | _rating = [[dictionary ftgp_nilledObjectForKey:@"rating"] floatValue]; 99 | _reference = [dictionary ftgp_nilledObjectForKey:@"reference"]; 100 | _types = [dictionary ftgp_nilledObjectForKey:@"types"]; 101 | 102 | _url = [NSURL URLWithString:[dictionary ftgp_nilledObjectForKey:@"url"]]; 103 | _websiteUrl = [NSURL URLWithString:[dictionary ftgp_nilledObjectForKey:@"website"]]; 104 | 105 | _utcOffset = [[dictionary ftgp_nilledObjectForKey:@"utc_offset"] doubleValue]; 106 | } 107 | 108 | @end -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIDictionaryRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIDictionaryRequest.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 21/11/13. 6 | // 7 | // 8 | // The MIT License (MIT) 9 | // 10 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is 17 | // furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | // THE SOFTWARE. 29 | 30 | #import 31 | 32 | #import "FTGooglePlacesAPICommon.h" 33 | 34 | /** 35 | * This is basic request accepting dictionary of keys and values used 36 | * directly in the request URL. 37 | * You shouldn't use this request directly. It is used just as simple 38 | * helper for a few very simple tasks like creating "nextPageToken" request 39 | * and for the purpose of unit testing 40 | */ 41 | 42 | @interface FTGooglePlacesAPIDictionaryRequest : NSObject 43 | 44 | /** 45 | * Creates new instance of simple Places API request. 46 | * 47 | * @param dictionary Dictionary of keys and values used directly in the request URL. 48 | * @param requestType Request type string returned as "requestTypeUrlString" 49 | * 50 | * @return Request instance. Returns nil if provided dictionary is nil or requestType is empty 51 | */ 52 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary requestType:(NSString *)requestType; 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIDictionaryRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIDictionaryRequest.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 21/11/13. 6 | // 7 | // 8 | // The MIT License (MIT) 9 | // 10 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is 17 | // furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | // THE SOFTWARE. 29 | 30 | #import "FTGooglePlacesAPIDictionaryRequest.h" 31 | 32 | @implementation FTGooglePlacesAPIDictionaryRequest { 33 | 34 | NSString *_placesAPIRequestMethod; 35 | NSDictionary *_placesAPIRequestParams; 36 | } 37 | 38 | - (instancetype)init 39 | { 40 | return [self initWithDictionary:nil requestType:nil]; 41 | } 42 | 43 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary requestType:(NSString *)requestType 44 | { 45 | if (!dictionary || [requestType length] == 0) { 46 | return nil; 47 | } 48 | 49 | self = [super init]; 50 | if (self) { 51 | _placesAPIRequestMethod = requestType; 52 | _placesAPIRequestParams = dictionary; 53 | } 54 | return self; 55 | } 56 | 57 | #pragma mark FTGooglePlacesAPIRequest protocol 58 | 59 | - (NSString *)placesAPIRequestMethod 60 | { 61 | return _placesAPIRequestMethod; 62 | } 63 | 64 | - (NSDictionary *)placesAPIRequestParams 65 | { 66 | return _placesAPIRequestParams; 67 | } 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPINearbySearchRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIRequest.h 3 | // 4 | // Created by Lukas Kukacka on 10/29/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | // Some of properties descriptions are copied from the Google Places API Documentation 31 | // at https://developers.google.com/places/documentation/search 32 | // Since this library aims to provide almost complete access to the places, 33 | // Google's docs descriptions are most relevant. 34 | 35 | #import 36 | #import 37 | 38 | #import "FTGooglePlacesAPICommon.h" 39 | 40 | /** 41 | * Class for encapsulating Google Places API "Nearby Search" request 42 | * See https://developers.google.com/places/documentation/search#PlaceSearchRequests 43 | * 44 | * A Nearby Search lets you search for Places within a specified area. 45 | * You can refine your search request by supplying keywords or specifying 46 | * the type of Place you are searching for. 47 | * 48 | * FTGooglePlacesAPINearbySearchRequest and FTGooglePlacesAPITextSearchRequest 49 | * have a lot of the same code. They don't have the common superclass 50 | * (and so have copied code) to make them totaly standalone. 51 | * We don't have any quarantee the single concrete request won't change 52 | * in the future (keys dependency or exclusivity etc.) and shared common 53 | * code and properties could cause a lot of trouble in that case 54 | */ 55 | 56 | /** 57 | * @see rankBy 58 | */ 59 | typedef NS_ENUM(NSUInteger, FTGooglePlacesAPIRequestParamRankBy) { 60 | /** 61 | * This option sorts results based on their importance. Ranking will favor prominent 62 | * places within the specified area. Prominence can be affected by a Place's ranking 63 | * in Google's index, the number of check-ins from your application, global popularity, 64 | * and other factors. 65 | */ 66 | FTGooglePlacesAPIRequestParamRankByProminence, 67 | /** 68 | * This option sorts results in ascending order by their distance from the specified 69 | * location. Ranking results by distance will set a fixed search radius of 50km. 70 | * One or more of keyword, name, or types is required. 71 | */ 72 | FTGooglePlacesAPIRequestParamRankByDistance 73 | }; 74 | 75 | 76 | @interface FTGooglePlacesAPINearbySearchRequest : NSObject 77 | 78 | // Required parameters 79 | 80 | /** 81 | * The latitude/longitude around which to retrieve Place information. 82 | * Property is read-only, value is set in init method 83 | */ 84 | @property (nonatomic, assign, readonly) CLLocationCoordinate2D locationCoordinate; 85 | 86 | /** 87 | * Defines the distance (in meters) within which to return Place results. 88 | * The maximum allowed radius is 50 000 meters. Default: 5 000 89 | */ 90 | @property (nonatomic, assign) NSUInteger radius; 91 | 92 | /* 93 | * Optional parameters 94 | * See https://developers.google.com/places/documentation/search for detailed descriptions 95 | */ 96 | 97 | /** 98 | * A term to be matched against all content that Google has indexed for this Place, 99 | * including but not limited to name, type, and address, as well as customer reviews 100 | * and other third-party content. 101 | */ 102 | @property (nonatomic, copy) NSString *keyword; 103 | 104 | /** 105 | * The language code, indicating in which language the results should be returned, if possible. 106 | * See http://spreadsheets.google.com/pub?key=p9pdwsai2hDMsLkXsoM05KQ&gid=1 for a complete list 107 | * 108 | * Default value is determined by active application language from NSUserDefaults 109 | * if available 110 | * (details: https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/Articles/ChoosingLocalizations.html) 111 | */ 112 | @property (nonatomic, copy) NSString *language; 113 | 114 | /** 115 | * One or more terms to be matched against the names of Places provided as an array 116 | * of NSStrings. Results will be restricted to those containing the passed name values. 117 | * Note that a Place may have additional names associated with it, beyond its listed name. 118 | * The API will try to match the passed name value against all of these names; 119 | * as a result, Places may be returned in the results whose listed names do not match 120 | * the search term, but whose associated names do. 121 | */ 122 | @property (nonatomic, copy) NSArray *names; 123 | 124 | /** 125 | * Restricts results to only those places within the specified range. Valid values 126 | * range between 0 (most affordable) to 4 (most expensive), inclusive. The exact amount 127 | * indicated by a specific value will vary from region to region. 128 | * When passed in value is >4, 4 is used. 129 | * Default value is NSUIntegerMax indicating this value will not be present in a request 130 | */ 131 | @property (nonatomic, assign) NSUInteger minPrice; 132 | @property (nonatomic, assign) NSUInteger maxPrice; 133 | 134 | /** 135 | * Returns only those Places that are open for business at the time 136 | * the query is sent. Places that do not specify opening hours 137 | * in the Google Places database will not be returned if you 138 | * include this parameter in your query. 139 | * Default: NO 140 | */ 141 | @property (nonatomic, assign) BOOL openNow; 142 | 143 | /** 144 | * Specifies the order in which results are listed. 145 | * Default: FTGooglePlacesAPIRequestParamRankByProminence 146 | */ 147 | @property (nonatomic, assign) FTGooglePlacesAPIRequestParamRankBy rankBy; 148 | 149 | /** 150 | * Restricts the results to Places matching at least one of the specified types. 151 | * Provide array of NSStrings 152 | * See https://developers.google.com/places/documentation/supported_types 153 | * for the list of all available types 154 | */ 155 | @property (nonatomic, copy) NSArray *types; 156 | 157 | /** 158 | * Returns the next 20 results from a previously run search. Setting a pagetoken parameter will execute 159 | * a search with the same parameters used previously 160 | */ 161 | @property (nonatomic, copy) NSString *pageToken; 162 | 163 | 164 | /** 165 | * Creates new intance of Places API request. 166 | * 167 | * @param locationCoordinate The latitude/longitude around which to retrieve Place information. This stands for "location" Google Places API parameter 168 | * 169 | * @return Request instance. If the provided location coordinates are invalid, returns nil 170 | */ 171 | - (instancetype)initWithLocationCoordinate:(CLLocationCoordinate2D)locationCoordinate; 172 | 173 | @end 174 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPINearbySearchRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIRequest.m 3 | // 4 | // Created by Lukas Kukacka on 10/29/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import "FTGooglePlacesAPINearbySearchRequest.h" 30 | 31 | static const NSUInteger kMaxRadius = 50000; 32 | 33 | /** 34 | * Private methods interface 35 | */ 36 | @interface FTGooglePlacesAPINearbySearchRequest (Private) 37 | 38 | - (NSString *)gpnsr_nameOfRankByParam:(FTGooglePlacesAPIRequestParamRankBy)rankByParam; 39 | 40 | @end 41 | 42 | #pragma mark - 43 | 44 | @implementation FTGooglePlacesAPINearbySearchRequest 45 | 46 | #pragma Lifecycle 47 | 48 | - (instancetype)initWithLocationCoordinate:(CLLocationCoordinate2D)locationCoordinate 49 | { 50 | // Validate location 51 | if (!CLLocationCoordinate2DIsValid(locationCoordinate)) { 52 | NSLog(@"WARNING: %s: Location (%f, %f) is invalid, returning nil", __PRETTY_FUNCTION__, locationCoordinate.latitude, locationCoordinate.longitude); 53 | return nil; 54 | } 55 | 56 | self = [super init]; 57 | if (self) 58 | { 59 | _locationCoordinate = locationCoordinate; 60 | 61 | // Default values 62 | _radius = 5000; 63 | _rankBy = FTGooglePlacesAPIRequestParamRankByProminence; 64 | _language = [FTGooglePlacesAPIUtils deviceLanguage]; 65 | _minPrice = NSUIntegerMax; 66 | _maxPrice = NSUIntegerMax; 67 | } 68 | return self; 69 | } 70 | 71 | #pragma mark Accessors 72 | 73 | - (void)setRadius:(NSUInteger)radius 74 | { 75 | [self willChangeValueForKey:@"radius"]; 76 | 77 | // Validate radius 78 | _radius = radius; 79 | if (_radius > kMaxRadius) { 80 | NSLog(@"WARNING: %s: Radius %ldm is too big. Maximum radius is %ldm, using maximum", __PRETTY_FUNCTION__, (unsigned long)radius, (unsigned long)kMaxRadius); 81 | _radius = kMaxRadius; 82 | } 83 | 84 | [self didChangeValueForKey:@"radius"]; 85 | } 86 | 87 | - (void)setMinPrice:(NSUInteger)minPrice 88 | { 89 | [self willChangeValueForKey:@"minPrice"]; 90 | 91 | // value ranges 0-4 92 | _minPrice = MAX(0,MIN(4, minPrice)); 93 | 94 | [self didChangeValueForKey:@"minPrice"]; 95 | } 96 | 97 | - (void)setMaxPrice:(NSUInteger)maxPrice 98 | { 99 | [self willChangeValueForKey:@"maxPrice"]; 100 | 101 | // value ranges 0-4 102 | _maxPrice = MAX(0,MIN(4, maxPrice)); 103 | 104 | [self didChangeValueForKey:@"maxPrice"]; 105 | } 106 | 107 | #pragma mark Superclass overrides 108 | 109 | - (NSString *)description 110 | { 111 | return [NSString stringWithFormat:@"<%@: %p> %@", [self class], self, [self placesAPIRequestParams]]; 112 | } 113 | 114 | #pragma mark FTGooglePlacesAPIRequest protocol 115 | 116 | - (NSString *)placesAPIRequestMethod 117 | { 118 | return @"nearbysearch"; 119 | } 120 | 121 | - (NSDictionary *)placesAPIRequestParams 122 | { 123 | NSMutableDictionary *params = [NSMutableDictionary dictionary]; 124 | 125 | // Required parameters 126 | params[@"location"] = [NSString stringWithFormat:@"%.7f,%.7f", _locationCoordinate.latitude, _locationCoordinate.longitude]; 127 | 128 | if (_rankBy != FTGooglePlacesAPIRequestParamRankByDistance) { 129 | params[@"radius"] = [NSString stringWithFormat:@"%ld", (unsigned long)_radius]; 130 | } 131 | 132 | // Optional parameters 133 | if (_keyword) { params[@"keyword"] = [_keyword stringByReplacingOccurrencesOfString:@" " withString:@"+"]; } 134 | if (_language) { params[@"language"] = _language; }; 135 | 136 | if ([_names count] > 0) { 137 | params[@"name"] = [_names componentsJoinedByString:@"|"]; 138 | } 139 | 140 | if (_minPrice != NSUIntegerMax) { 141 | params[@"minprice"] = [NSString stringWithFormat:@"%ld", (unsigned long)_minPrice]; 142 | } 143 | 144 | if (_maxPrice != NSUIntegerMax) { 145 | params[@"maxprice"] = [NSString stringWithFormat:@"%ld", (unsigned long)_maxPrice]; 146 | } 147 | 148 | if (_openNow) { 149 | params[@"opennow"] = [NSNull null]; 150 | } 151 | 152 | NSString *rankBy = [self gpnsr_nameOfRankByParam:_rankBy]; 153 | if (rankBy) { 154 | params[@"rankby"] = rankBy; 155 | } 156 | 157 | if ([_types count] > 0) { 158 | params[@"types"] = [_types componentsJoinedByString:@"|"]; 159 | } 160 | 161 | if (_pageToken) { params[@"pagetoken"] = _pageToken; } 162 | 163 | return [params copy]; 164 | } 165 | 166 | @end 167 | 168 | #pragma mark - Private methods category 169 | 170 | @implementation FTGooglePlacesAPINearbySearchRequest (Private) 171 | 172 | - (NSString *)gpnsr_nameOfRankByParam:(FTGooglePlacesAPIRequestParamRankBy)rankByParam 173 | { 174 | NSString *name = nil; 175 | 176 | switch (rankByParam) 177 | { 178 | case FTGooglePlacesAPIRequestParamRankByProminence: 179 | name = @"prominence"; 180 | break; 181 | case FTGooglePlacesAPIRequestParamRankByDistance: 182 | name = @"distance"; 183 | break; 184 | } 185 | 186 | return name; 187 | } 188 | 189 | @end 190 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIResponse.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIResponse.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 29/11/13. 6 | // 7 | // 8 | // The MIT License (MIT) 9 | // 10 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is 17 | // furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | // THE SOFTWARE. 29 | // 30 | // 31 | // Some of properties descriptions are copied from the Google Places API Documentation 32 | // at https://developers.google.com/places/documentation/search 33 | // Since this library aims to provide almost complete access to the places, 34 | // Google's docs descriptions are most relevant. 35 | 36 | #import 37 | 38 | @protocol FTGooglePlacesAPIRequest; 39 | 40 | 41 | typedef NS_ENUM(NSUInteger, FTGooglePlacesAPIResponseStatus) { 42 | FTGooglePlacesAPIResponseStatusUnknown = 0, // Default or UNKNOWN_ERROR 43 | FTGooglePlacesAPIResponseStatusOK, // OK 44 | FTGooglePlacesAPIResponseStatusNoResults, // ZERO_RESULTS 45 | FTGooglePlacesAPIResponseStatusAPILimitExceeded, // OVER_QUERY_LIMIT 46 | FTGooglePlacesAPIResponseStatusRequestDenied, // REQUEST_DENIED 47 | FTGooglePlacesAPIResponseStatusInvalidRequest, // INVALID_REQUEST 48 | FTGooglePlacesAPIResponseStatusNotFound // NOT_FOUND 49 | }; 50 | 51 | /** 52 | * Basic "abstract" object for encapsulating Google Places API response. 53 | * DO NOT USE this class DIRECTLY. It is meant to be more of a abstract 54 | * class, which is unfortunatelly not supported in Objective-C. 55 | * 56 | * This class and all of its subclasses are Key-Value coding ready. 57 | * You can use |valueForKeyPath:| for accessing values from the original 58 | * response dictionary. 59 | */ 60 | @interface FTGooglePlacesAPIResponse : NSObject 61 | 62 | /** 63 | * Request which resulted in this response 64 | */ 65 | @property (nonatomic, strong, readonly) id request; 66 | 67 | /** 68 | * NSDictionary returned as a response and passed in the initialization 69 | */ 70 | @property (nonatomic, strong, readonly) NSDictionary *originalResponseDictionary; 71 | 72 | /** 73 | * Contains response status 74 | * Details: https://developers.google.com/places/documentation/search#PlaceSearchStatusCodes 75 | */ 76 | @property (nonatomic, assign, readonly) FTGooglePlacesAPIResponseStatus status; 77 | 78 | /** 79 | * Designated initializer with nil class 80 | * @see initWithDictionary:request:resultsItemClass: 81 | */ 82 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 83 | request:(id)request; 84 | 85 | 86 | + (NSString *)localizedNameOfStatus:(FTGooglePlacesAPIResponseStatus)status; 87 | + (NSString *)localizedDescriptionForStatus:(FTGooglePlacesAPIResponseStatus)status; 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIResponse.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIResponse.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 29/11/13. 6 | // 7 | // 8 | // The MIT License (MIT) 9 | // 10 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 11 | // 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy 13 | // of this software and associated documentation files (the "Software"), to deal 14 | // in the Software without restriction, including without limitation the rights 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | // copies of the Software, and to permit persons to whom the Software is 17 | // furnished to do so, subject to the following conditions: 18 | // 19 | // The above copyright notice and this permission notice shall be included in 20 | // all copies or substantial portions of the Software. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | // THE SOFTWARE. 29 | 30 | 31 | #import "FTGooglePlacesAPIResponse.h" 32 | #import "FTGooglePlacesAPICommon.h" 33 | 34 | /** 35 | * Private methods interface 36 | */ 37 | @interface FTGooglePlacesAPIResponse (Private) 38 | 39 | - (FTGooglePlacesAPIResponseStatus)ftgpr_responseStatusFromString:(NSString *)string; 40 | 41 | @end 42 | 43 | #pragma mark - 44 | 45 | @implementation FTGooglePlacesAPIResponse 46 | 47 | #pragma mark Lifecycle 48 | 49 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 50 | request:(id)request 51 | { 52 | if ([dictionary count] == 0) { 53 | return nil; 54 | } 55 | 56 | self = [super init]; 57 | if (self) 58 | { 59 | _originalResponseDictionary = dictionary; 60 | _request = request; 61 | 62 | // Parsing of common values 63 | NSString *statusString = [dictionary ftgp_nilledObjectForKey:@"status"]; 64 | _status = [self ftgpr_responseStatusFromString:statusString]; 65 | } 66 | return self; 67 | } 68 | 69 | #pragma mark Superclass overrides 70 | 71 | - (id)valueForUndefinedKey:(NSString *)key 72 | { 73 | // Try to find underfined key in a original dictionary 74 | id value = [_originalResponseDictionary valueForKeyPath:key]; 75 | 76 | // If it is there, return it 77 | if (value) { 78 | return value; 79 | } 80 | 81 | // Else call default implementation 82 | return [super valueForUndefinedKey:key]; 83 | } 84 | 85 | #pragma mark Class methods 86 | 87 | + (NSString *)localizedNameOfStatus:(FTGooglePlacesAPIResponseStatus)status 88 | { 89 | NSString *name; 90 | 91 | switch (status) 92 | { 93 | case FTGooglePlacesAPIResponseStatusOK: 94 | name = NSLocalizedString(@"OK", @"FTGooglePlacesAPISearchResponse name of status FTGooglePlacesAPIResponseStatusOK"); 95 | break; 96 | case FTGooglePlacesAPIResponseStatusNoResults: 97 | name = NSLocalizedString(@"No Results", @"FTGooglePlacesAPISearchResponse name of status FTGooglePlacesAPIResponseStatusNoResults"); 98 | break; 99 | case FTGooglePlacesAPIResponseStatusAPILimitExceeded: 100 | name = NSLocalizedString(@"API Limit Exceeded", @"FTGooglePlacesAPISearchResponse name of status FTGooglePlacesAPIResponseStatusAPILimitExceeded"); 101 | break; 102 | case FTGooglePlacesAPIResponseStatusRequestDenied: 103 | name = NSLocalizedString(@"Request Denied", @"FTGooglePlacesAPISearchResponse name of status FTGooglePlacesAPIResponseStatusRequestDenied"); 104 | break; 105 | case FTGooglePlacesAPIResponseStatusInvalidRequest: 106 | name = NSLocalizedString(@"Invalid Request", @"FTGooglePlacesAPISearchResponse name of status FTGooglePlacesAPIResponseStatusInvalidRequest"); 107 | break; 108 | case FTGooglePlacesAPIResponseStatusNotFound: 109 | name = NSLocalizedString(@"Place not found", @"FTGooglePlacesAPISearchResponse name of status FTGooglePlacesAPIResponseStatusNotFound"); 110 | break; 111 | case FTGooglePlacesAPIResponseStatusUnknown: 112 | name = NSLocalizedString(@"Unknown", @"FTGooglePlacesAPISearchResponse name of status FTGooglePlacesAPIResponseStatusUnknown"); 113 | break; 114 | } 115 | 116 | return name; 117 | } 118 | 119 | + (NSString *)localizedDescriptionForStatus:(FTGooglePlacesAPIResponseStatus)status 120 | { 121 | NSString *description; 122 | 123 | switch (status) 124 | { 125 | case FTGooglePlacesAPIResponseStatusOK: 126 | description = NSLocalizedString(@"Everything went fine. At least one result was found.", nil); 127 | break; 128 | case FTGooglePlacesAPIResponseStatusNoResults: 129 | description = NSLocalizedString(@"No results were found.", nil); 130 | break; 131 | case FTGooglePlacesAPIResponseStatusAPILimitExceeded: 132 | description = NSLocalizedString(@"Google Places API requests quota was exceeded.", nil); 133 | break; 134 | case FTGooglePlacesAPIResponseStatusRequestDenied: 135 | description = NSLocalizedString(@"Request was denied by Google Places API.", nil); 136 | break; 137 | case FTGooglePlacesAPIResponseStatusInvalidRequest: 138 | description = NSLocalizedString(@"Request was invalid. Either \"location\" or \"radius\" parameter is probably missing.", nil); 139 | break; 140 | case FTGooglePlacesAPIResponseStatusNotFound: 141 | description = NSLocalizedString(@"Referenced place was not found.", nil); 142 | break; 143 | case FTGooglePlacesAPIResponseStatusUnknown: 144 | description = NSLocalizedString(@"Google Places API returned unknown status code.", nil); 145 | break; 146 | } 147 | 148 | return description; 149 | } 150 | 151 | @end 152 | 153 | 154 | #pragma mark - Private methods category 155 | 156 | @implementation FTGooglePlacesAPIResponse (Private) 157 | 158 | - (FTGooglePlacesAPIResponseStatus)ftgpr_responseStatusFromString:(NSString *)string 159 | { 160 | if ([string length] == 0) { 161 | return FTGooglePlacesAPIResponseStatusUnknown; 162 | } 163 | 164 | if ([string isEqualToString:@"OK"]) { 165 | return FTGooglePlacesAPIResponseStatusOK; 166 | } else if ([string isEqualToString:@"ZERO_RESULTS"]) { 167 | return FTGooglePlacesAPIResponseStatusNoResults; 168 | } 169 | else if ([string isEqualToString:@"OVER_QUERY_LIMIT"]) { 170 | return FTGooglePlacesAPIResponseStatusAPILimitExceeded; 171 | } 172 | else if ([string isEqualToString:@"REQUEST_DENIED"]) { 173 | return FTGooglePlacesAPIResponseStatusRequestDenied; 174 | } 175 | else if ([string isEqualToString:@"INVALID_REQUEST"]) { 176 | return FTGooglePlacesAPIResponseStatusInvalidRequest; 177 | } 178 | else if ([string isEqualToString:@"NOT_FOUND"]) { 179 | return FTGooglePlacesAPIResponseStatusNotFound; 180 | } else { 181 | return FTGooglePlacesAPIResponseStatusUnknown; 182 | } 183 | } 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPISearchResponse.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIResponse.h 3 | // 4 | // Created by Lukas Kukacka on 10/29/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | // 29 | // 30 | // Some of properties descriptions are copied from the Google Places API Documentation 31 | // at https://developers.google.com/places/documentation/search 32 | // Since this library aims to provide almost complete access to the places, 33 | // Google's docs descriptions are most relevant. 34 | 35 | //#import 36 | #import "FTGooglePlacesAPIResponse.h" 37 | #import 38 | 39 | @protocol FTGooglePlacesAPIRequest; 40 | 41 | /** 42 | * Object encapsulationg response from the Google Places API request 43 | * Properties correpond to the keys in a response 44 | * Details: https://developers.google.com/places/documentation/search#PlaceSearchResponses 45 | */ 46 | @interface FTGooglePlacesAPISearchResponse : FTGooglePlacesAPIResponse 47 | 48 | /** 49 | * Array of results items. Items are instances of class provided in init... method 50 | */ 51 | @property (nonatomic, strong, readonly) NSArray *results; 52 | 53 | /** 54 | * Token which can be used for paging results like this one. 55 | * When this token is passed in request, response will containt next results 56 | * for the same request. 57 | * If this value is nil, no next page is available (none was returned in respone) 58 | */ 59 | @property (nonatomic, strong, readonly) NSString *nextPageToken; 60 | 61 | /** 62 | * Contain a set of attributions about this listing which must be displayed to the user 63 | * to conform Google Terms and Conditions 64 | */ 65 | @property (nonatomic, strong, readonly) NSArray *htmlAttributions; 66 | 67 | /** 68 | * Designated initializer for a response. 69 | * 70 | * @param dictionary Dictionary from which the response will be parsed. 71 | * @param request Request which resulted to this response 72 | * @param resultsItemClass Class of the result item to be used. You can either subclass of FTGooglePlacesAPISearchResultItem or nil. When nil is used, default FTGooglePlacesAPISearchResultItem will be used 73 | * 74 | * @return Initialized instance of the response parsed from the dictionary or nil if anything failed 75 | */ 76 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 77 | request:(id)request 78 | resultsItemClass:(Class)resultsItemClass; 79 | 80 | /** 81 | * Creates request which can be used for paging this response. It uses 82 | * "nextPageToken" to create request which will ask for next results of this 83 | * response. 84 | * 85 | * @return Request object which can be passed directly as a request to the service 86 | * or nil if there is no "nextPageToken" 87 | */ 88 | - (id)nextPageRequest; 89 | 90 | /** 91 | * Convenience method for checking whether there is any "nextPageToken" 92 | * allowing us to ask for next page of results for this request 93 | * 94 | * @return YES if there is any next page of results 95 | */ 96 | - (BOOL)hasNextPage; 97 | 98 | @end -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPISearchResponse.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIResponse.m 3 | // 4 | // Created by Lukas Kukacka on 10/29/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import "FTGooglePlacesAPISearchResponse.h" 30 | #import "FTGooglePlacesAPICommon.h" 31 | #import "FTGooglePlacesAPISearchResultItem.h" 32 | #import "FTGooglePlacesAPIDictionaryRequest.h" 33 | 34 | /** 35 | * Private methods interface 36 | */ 37 | @interface FTGooglePlacesAPISearchResponse (Private) 38 | 39 | - (void)ftgpr_importDictionary:(NSDictionary *)dictionary; 40 | - (NSArray *)ftgpr_parseResultsItemFromArray:(NSArray *)array; 41 | 42 | @end 43 | 44 | #pragma mark - 45 | 46 | @implementation FTGooglePlacesAPISearchResponse { 47 | __weak Class _resultsItemClass; 48 | } 49 | 50 | #pragma mark Lifecycle 51 | 52 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 53 | request:(id)request 54 | { 55 | return [self initWithDictionary:dictionary request:request resultsItemClass:nil]; 56 | } 57 | 58 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 59 | request:(id)request 60 | resultsItemClass:(Class)resultsItemClass 61 | { 62 | // Either there is no custom class or it has to be subclass of FTGooglePlacesAPIResponse so 63 | // the code can rely there will be required properties 64 | // If this expectation fails, it is clearly programmers fault, so let him know! 65 | if (resultsItemClass && 66 | ![resultsItemClass isSubclassOfClass:[FTGooglePlacesAPISearchResultItem class]]) 67 | { 68 | @throw [NSException exceptionWithName:NSInvalidArgumentException 69 | reason:@"Custom response item class in FTGooglePlacesAPISearchResponse must be a subclass of FTGooglePlacesAPIResponse" 70 | userInfo:nil]; 71 | } 72 | 73 | self = [super initWithDictionary:dictionary request:request]; 74 | if (self) 75 | { 76 | _resultsItemClass = resultsItemClass; 77 | [self ftgpr_importDictionary:dictionary]; 78 | } 79 | return self; 80 | } 81 | 82 | #pragma mark - Public interface 83 | 84 | - (id)nextPageRequest 85 | { 86 | if (![self hasNextPage]) { 87 | return nil; 88 | } 89 | 90 | NSDictionary *dictionary = @{@"pagetoken": _nextPageToken}; 91 | 92 | FTGooglePlacesAPIDictionaryRequest *request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:dictionary requestType:[self.request placesAPIRequestMethod]]; 93 | 94 | return request; 95 | } 96 | 97 | - (BOOL)hasNextPage 98 | { 99 | return ([_nextPageToken length] > 0); 100 | } 101 | 102 | @end 103 | 104 | #pragma mark - Private methods category 105 | 106 | @implementation FTGooglePlacesAPISearchResponse (Private) 107 | 108 | - (void)ftgpr_importDictionary:(NSDictionary *)dictionary 109 | { 110 | _htmlAttributions = [dictionary ftgp_nilledObjectForKey:@"html_attributions"]; 111 | _nextPageToken = [dictionary ftgp_nilledObjectForKey:@"next_page_token"]; 112 | 113 | NSArray *results = [dictionary ftgp_nilledObjectForKey:@"results"]; 114 | _results = [self ftgpr_parseResultsItemFromArray:results]; 115 | } 116 | 117 | - (NSArray *)ftgpr_parseResultsItemFromArray:(NSArray *)array 118 | { 119 | if (![array isKindOfClass:[NSArray class]] || [array count] == 0) { 120 | return nil; 121 | } 122 | 123 | // If there is a custom class response item defined, use that one 124 | // else use the default response item class 125 | Class resultsItemClass = (_resultsItemClass ?: [FTGooglePlacesAPISearchResultItem class]); 126 | 127 | NSMutableArray *results = [NSMutableArray arrayWithCapacity:[array count]]; 128 | 129 | for (NSDictionary *resultDictionary in array) 130 | { 131 | id resultItem = [[resultsItemClass alloc] initWithDictionary:resultDictionary]; 132 | [results addObject:resultItem]; 133 | } 134 | 135 | return [results copy]; 136 | } 137 | 138 | @end -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPISearchResultItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPISearchResultItem.h 3 | // 4 | // Created by Lukas Kukacka on 10/30/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import 30 | #import 31 | 32 | /** 33 | * Class encapsulating concrete record of Google Places API Response (item in the "results" array) 34 | */ 35 | typedef NS_ENUM(NSUInteger, FTGooglePlacesAPISearchResultItemOpenedState) { 36 | FTGooglePlacesAPISearchResultItemOpenedStateUnknown, // Value was not in the response 37 | FTGooglePlacesAPISearchResultItemOpenedStateOpened, // Item is opened 38 | FTGooglePlacesAPISearchResultItemOpenedStateClosed // Item is closed 39 | }; 40 | 41 | @interface FTGooglePlacesAPISearchResultItem : NSObject 42 | 43 | @property (nonatomic, strong, readonly) NSString *placeId; // Google's id for the item 44 | @property (nonatomic, strong, readonly) NSString *name; 45 | 46 | /** 47 | * CLLocation representing results coordinates. If the returned coordinates were invalid, 48 | * location will be nil 49 | */ 50 | @property (nonatomic, strong, readonly) CLLocation *location; 51 | 52 | @property (nonatomic, strong, readonly) NSString *addressString; // "vicinity" from the response 53 | @property (nonatomic, assign, readonly) FTGooglePlacesAPISearchResultItemOpenedState openedState; 54 | @property (nonatomic, strong, readonly) NSString *iconImageUrl; 55 | @property (nonatomic, assign, readonly) CGFloat rating; 56 | @property (nonatomic, strong, readonly) NSString *reference; 57 | @property (nonatomic, strong, readonly) NSArray *types; 58 | 59 | /** 60 | * You can access complete response dictionary using this property. 61 | * In case implementation changes in a future and there will be new values 62 | * for example, you can access them directly using this property even 63 | * if the library is not ready for those yet 64 | */ 65 | @property (nonatomic, strong, readonly) NSDictionary *originalDictionaryRepresentation; 66 | 67 | /** 68 | * Designated initializer contructs intance from a Google Places API 69 | * response dictionary 70 | * 71 | * @param dictionary Response dictionary (parsed JSON/XML) 72 | * 73 | * @return Instance of a result 74 | */ 75 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary; 76 | 77 | /** 78 | * Method for comparing equality of two result item objects. 79 | * Objects are considered to be equal if their "placeId" equals. 80 | * 81 | * @param item Item to compare with 82 | * 83 | * @return YES if both objects are considered to be equal 84 | */ 85 | - (BOOL)isEqualToSearchResultItem:(FTGooglePlacesAPISearchResultItem *)item; 86 | 87 | 88 | /** 89 | * Deprecations 90 | */ 91 | @property (nonatomic, strong, readonly) NSString *itemId DEPRECATED_MSG_ATTRIBUTE("Deprecated in API by Google as of June 24, 2014. Use placeId instead. See https://developers.google.com/places/documentation/details#deprecation for more info."); 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPISearchResultItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPISearchResultItem.m 3 | // 4 | // Created by Lukas Kukacka on 10/30/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import "FTGooglePlacesAPISearchResultItem.h" 30 | #import "FTGooglePlacesAPICommon.h" 31 | 32 | /** 33 | * Private methods interface 34 | */ 35 | @interface FTGooglePlacesAPISearchResultItem (Private) 36 | 37 | - (void)ftgpi_importDictionary:(NSDictionary *)dictionary; 38 | 39 | @end 40 | 41 | 42 | #pragma mark - 43 | 44 | @implementation FTGooglePlacesAPISearchResultItem 45 | 46 | #pragma mark Lifecycle 47 | 48 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary 49 | { 50 | self = [super init]; 51 | if (self) 52 | { 53 | _originalDictionaryRepresentation = dictionary; 54 | [self ftgpi_importDictionary:dictionary]; 55 | 56 | // Object cannot be valid with no id 57 | if ([_placeId length] == 0) { 58 | return nil; 59 | } 60 | 61 | } 62 | return self; 63 | } 64 | 65 | #pragma mark Superclass overrides 66 | 67 | - (NSString *)description 68 | { 69 | return [NSString stringWithFormat:@"<%@: %p> \"%@\", Location: (%f,%f), Address: %@", [self class], self, _name, _location.coordinate.latitude, _location.coordinate.longitude, _addressString]; 70 | } 71 | 72 | - (NSString *)debugDescription 73 | { 74 | return [NSString stringWithFormat:@"<%@: %p> %@", [self class], self, _originalDictionaryRepresentation]; 75 | } 76 | 77 | - (NSUInteger)hash 78 | { 79 | // Google's ID is unique identifier of the item 80 | return [_placeId hash]; 81 | } 82 | 83 | - (BOOL)isEqual:(id)object 84 | { 85 | if ([object class] == [self class]) { 86 | return [self isEqualToSearchResultItem:(FTGooglePlacesAPISearchResultItem *)object]; 87 | } else { 88 | return [super isEqual:object]; 89 | } 90 | } 91 | 92 | #pragma mark Public interface 93 | 94 | - (BOOL)isEqualToSearchResultItem:(FTGooglePlacesAPISearchResultItem *)item 95 | { 96 | return [_placeId isEqualToString:item.placeId]; 97 | } 98 | 99 | @end 100 | 101 | #pragma mark - Private category 102 | 103 | @implementation FTGooglePlacesAPISearchResultItem (Private) 104 | 105 | // Private methods are prefixed to avoid methods names collisions on subclassing 106 | 107 | - (void)ftgpi_importDictionary:(NSDictionary *)dictionary 108 | { 109 | _originalDictionaryRepresentation = dictionary; 110 | 111 | _placeId = [dictionary ftgp_nilledObjectForKey:@"place_id"]; 112 | _name = [dictionary ftgp_nilledObjectForKey:@"name"]; 113 | 114 | 115 | // location is nil by default on errors or returned invalid values of Lat and Lon 116 | _location = nil; 117 | 118 | // Extract Lat and Lon from the response. If combination of Lat and Lon make 119 | // valid coordinates, create CLLocation object from them 120 | NSDictionary *geometryDict = [dictionary ftgp_nilledObjectForKey:@"geometry"]; 121 | NSNumber *latitudeNumber = [geometryDict ftgp_nilledValueForKeyPath:@"location.lat"]; 122 | NSNumber *longitudeNumber = [geometryDict ftgp_nilledValueForKeyPath:@"location.lng"]; 123 | if (geometryDict && latitudeNumber && longitudeNumber) 124 | { 125 | CLLocationDegrees latitude = [latitudeNumber doubleValue]; 126 | CLLocationDegrees longitude = [longitudeNumber doubleValue]; 127 | 128 | // If the LAT and LON are valid, create CLLocation object 129 | CLLocationCoordinate2D locationCoordinate = CLLocationCoordinate2DMake(latitude, longitude); 130 | 131 | if (CLLocationCoordinate2DIsValid(locationCoordinate)) { 132 | _location = [[CLLocation alloc] initWithLatitude:latitude longitude:longitude]; 133 | } 134 | 135 | } 136 | 137 | _addressString = [dictionary ftgp_nilledObjectForKey:@"vicinity"]; 138 | 139 | // Openeed state may or may not be present 140 | NSNumber *openedState = [dictionary ftgp_nilledValueForKeyPath:@"opening_hours.open_now"]; 141 | if (openedState) { 142 | _openedState = ([openedState boolValue] ? FTGooglePlacesAPISearchResultItemOpenedStateOpened : FTGooglePlacesAPISearchResultItemOpenedStateClosed); 143 | } else { 144 | _openedState = FTGooglePlacesAPISearchResultItemOpenedStateUnknown; 145 | } 146 | 147 | _iconImageUrl = [dictionary ftgp_nilledObjectForKey:@"icon"]; 148 | _rating = [[dictionary ftgp_nilledObjectForKey:@"rating"] floatValue]; 149 | _reference = [dictionary ftgp_nilledObjectForKey:@"reference"]; 150 | _types = [dictionary ftgp_nilledObjectForKey:@"types"]; 151 | 152 | // Deprecated, left for backwards compatibility 153 | _itemId = [dictionary ftgp_nilledObjectForKey:@"id"]; 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIService.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIService.h 3 | // 4 | // Created by Lukas Kukacka on 10/29/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import 30 | 31 | #import "FTGooglePlacesAPICommon.h" 32 | 33 | @class FTGooglePlacesAPISearchResponse; 34 | @class FTGooglePlacesAPIDetailResponse; 35 | 36 | extern NSString *const FTGooglePlacesAPIBaseURL; 37 | 38 | typedef void (^FTGooglePlacesAPISearchRequestCompletionHandler)(FTGooglePlacesAPISearchResponse *response, NSError *error); 39 | 40 | typedef void (^FTGooglePlacesAPIDetailRequestCompletionhandler)(FTGooglePlacesAPIDetailResponse *response, NSError *error); 41 | 42 | /** 43 | * This class provides encapsulated functionality for communication with Google Places API 44 | */ 45 | @interface FTGooglePlacesAPIService : NSObject 46 | 47 | /** 48 | * Provides API key for the service. Key must be provided before any requests are porformed, 49 | * preferably in the app delegate. 50 | * 51 | * @param APIKey API key to be used for authentication of the requests. Copied. 52 | */ 53 | + (void)provideAPIKey:(NSString *)APIKey; 54 | 55 | /** 56 | * This optional method can be used to instruct the service to return search 57 | * request's response items as an instances of custom item subclass. 58 | * 59 | * This can be particularly usefull is you want your subclass be for example 60 | * map annotation. In that case, just subclass FTGooglePlacesAPIResponse 61 | * and implement MKAnnotation protocol 62 | * 63 | * Provided class has to be subclass of FTGooglePlacesAPIResponse 64 | * - always call super in the subclass 65 | * - override designated initializer 66 | * 67 | * @param responseClass Subclass of the API response class to use 68 | */ 69 | + (void)registerSearchResultItemClass:(Class)itemClass; 70 | 71 | /** 72 | * Asks the service to execute the given Google Places API Places Search request. 73 | * 74 | * @param request Request object implementing FTGooglePlacesAPIRequest protocol. This will probably be either FTGooglePlacesAPINearbySearchRequest or FTGooglePlacesAPITextSearchRequest, but you are free to provide own request implementing requred FTGooglePlacesAPIRequest protocol 75 | * @param completionBlock Completion block to be called after the request was finished. If everything went without problems, response will be non-nill and error will be nil. In case of failure, response will be nil and error will be either AFNetworking error caused by networking problem or error with FTGooglePlacesAPIErrorDomain domain indicating that the networking request was successfull, but Google Places API responded with non-OK status code 76 | */ 77 | + (void)executeSearchRequest:(id)request 78 | withCompletionHandler:(FTGooglePlacesAPISearchRequestCompletionHandler)completion; 79 | 80 | /** 81 | * Asks the service to execute the given Google Places API Places Detail request. 82 | * 83 | * @param request Request object implementing FTGooglePlacesAPIRequest protocol. This will probably be instance of FTGooglePlacesAPIDetailRequest, but you are free to provide own request implementing requred FTGooglePlacesAPIRequest protocol 84 | * @param completion Completion block to be called after the request was finished. If everything went without problems, response will be non-nill and error will be nil. In case of failure, response will be nil and error will be either AFNetworking error caused by networking problem or error with FTGooglePlacesAPIErrorDomain domain indicating that the networking request was successfull, but Google Places API responded with non-OK status code 85 | */ 86 | + (void)executeDetailRequest:(id)request 87 | withCompletionHandler:(FTGooglePlacesAPIDetailRequestCompletionhandler)completion; 88 | 89 | /** 90 | * If set to YES and running in debug mode (#ifdef DEBUG), service will print some information 91 | * info console, including: 92 | * - Complete URL of request sent to Google Places API 93 | * - Status of response and number of result items in a response 94 | * 95 | * @param enabled YES if logs should be printed to the console in DEBUG mode 96 | */ 97 | + (void)setDebugLoggingEnabled:(BOOL)enabled; 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPIService.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIService.m 3 | // 4 | // Created by Lukas Kukacka on 10/29/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import "FTGooglePlacesAPIService.h" 30 | 31 | #import "AFNetworking.h" 32 | #import "FTGooglePlacesAPISearchResponse.h" 33 | #import "FTGooglePlacesAPIDetailResponse.h" 34 | 35 | 36 | /** 37 | * Helper logging macro for printing log messages only in DEBUG mode and when 38 | * logging is enabled by settings. Works the same as NSLog() 39 | * 40 | * @param format Ouput string format 41 | * @param ... Output string parameters 42 | */ 43 | #ifdef DEBUG 44 | #define FTGPServiceLog(format, ...) if (FTGooglePlacesAPIDebugLoggingEnabled) { NSLog(format, ##__VA_ARGS__); } 45 | #else 46 | #define FTGPServiceLog(format, ...) ((void)0) 47 | #endif 48 | 49 | 50 | NSString *const FTGooglePlacesAPIBaseURL = @"https://maps.googleapis.com/maps/api/place/"; 51 | 52 | 53 | static BOOL FTGooglePlacesAPIDebugLoggingEnabled; 54 | 55 | 56 | #pragma mark - Private interface definition 57 | 58 | /** 59 | * Class continuation category with private properties 60 | */ 61 | @interface FTGooglePlacesAPIService () 62 | 63 | /** 64 | * AFNetworking request manager. This manager is lazily intitialized in custom getter. 65 | * Default implementation is initialized with base URL of Google Places API 66 | */ 67 | @property (nonatomic, strong) AFHTTPRequestOperationManager *httpRequestOperationManager; 68 | 69 | @property (nonatomic, copy) NSString *apiKey; 70 | @property (nonatomic, weak) Class searchResultsItemClass; 71 | 72 | /** 73 | * Shared singleton instance 74 | */ 75 | + (FTGooglePlacesAPIService *)sharedService; 76 | 77 | @end 78 | 79 | 80 | /** 81 | * Private methods interface 82 | */ 83 | @interface FTGooglePlacesAPIService (Private) 84 | 85 | + (NSError *)ftgp_errorForResponseStatus:(FTGooglePlacesAPIResponseStatus)status; 86 | 87 | @end 88 | 89 | 90 | #pragma mark - 91 | #pragma mark - Implementation 92 | 93 | @implementation FTGooglePlacesAPIService 94 | 95 | #pragma mark Lifecycle 96 | 97 | + (FTGooglePlacesAPIService *)sharedService 98 | { 99 | static FTGooglePlacesAPIService *SharedInstance; 100 | 101 | static dispatch_once_t onceToken; 102 | dispatch_once(&onceToken, ^{ 103 | SharedInstance = [[FTGooglePlacesAPIService alloc] init]; 104 | }); 105 | 106 | return SharedInstance; 107 | } 108 | 109 | - (id)init 110 | { 111 | self = [super init]; 112 | if (self) { 113 | _apiKey = nil; 114 | _searchResultsItemClass = nil; 115 | FTGooglePlacesAPIDebugLoggingEnabled = NO; 116 | } 117 | return self; 118 | } 119 | 120 | #pragma mark - Accessors 121 | 122 | - (void)setApiKey:(NSString *)apiKey 123 | { 124 | NSAssert([apiKey length] > 0, @"You must provide non-empty API key"); 125 | _apiKey = apiKey; 126 | } 127 | 128 | #pragma mark Private 129 | 130 | - (AFHTTPRequestOperationManager *)httpRequestOperationManager 131 | { 132 | if (!_httpRequestOperationManager) 133 | { 134 | NSURL *baseUrl = [NSURL URLWithString:FTGooglePlacesAPIBaseURL]; 135 | _httpRequestOperationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseUrl]; 136 | _httpRequestOperationManager.requestSerializer = [AFHTTPRequestSerializer serializer]; 137 | } 138 | 139 | return _httpRequestOperationManager; 140 | } 141 | 142 | #pragma mark - Public class methods 143 | 144 | + (void)provideAPIKey:(NSString *)APIKey 145 | { 146 | [[[self class] sharedService] setApiKey:APIKey]; 147 | } 148 | 149 | + (void)registerSearchResultItemClass:(Class)itemClass 150 | { 151 | [[[self class] sharedService] setSearchResultsItemClass:itemClass]; 152 | } 153 | 154 | + (void)executeSearchRequest:(id)request 155 | withCompletionHandler:(FTGooglePlacesAPISearchRequestCompletionHandler)completion 156 | { 157 | [[self class] executeRequest:request withCompletionHandler:^(NSDictionary *responseObject, NSError *error) { 158 | 159 | // Networing, parsing or other general error 160 | if (error) { 161 | completion(nil, error); 162 | return; 163 | } 164 | 165 | // Parse response 166 | Class resultsItemClass = [[[self class] sharedService] searchResultsItemClass]; 167 | 168 | FTGooglePlacesAPISearchResponse *response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:responseObject request:request resultsItemClass:resultsItemClass]; 169 | 170 | FTGPServiceLog(@"%@ received Search response. Status: %@, number of results: %ld", [self class], [FTGooglePlacesAPISearchResponse localizedNameOfStatus:response.status], (unsigned long)[response.results count]); 171 | 172 | // Check if everything went OK 173 | if (response && response.status == FTGooglePlacesAPIResponseStatusOK) { 174 | completion(response, nil); 175 | } 176 | // If network request was successfull, but Google Places API 177 | // responded with error status code 178 | else { 179 | completion(response, [[self class] ftgp_errorForResponseStatus:response.status]); 180 | } 181 | }]; 182 | } 183 | 184 | + (void)executeDetailRequest:(id)request 185 | withCompletionHandler:(FTGooglePlacesAPIDetailRequestCompletionhandler)completion 186 | { 187 | [[self class] executeRequest:request withCompletionHandler:^(NSDictionary *responseObject, NSError *error) { 188 | 189 | // Networing, parsing or other general error 190 | if (error) { 191 | completion(nil, error); 192 | return; 193 | } 194 | 195 | // Try to parse response to object 196 | FTGooglePlacesAPIDetailResponse *response = [[FTGooglePlacesAPIDetailResponse alloc] initWithDictionary:responseObject request:request]; 197 | 198 | FTGPServiceLog(@"%@ received Detail response. Status: %@", [self class], [FTGooglePlacesAPISearchResponse localizedNameOfStatus:response.status]); 199 | 200 | // Check if everything went OK 201 | if (response && response.status == FTGooglePlacesAPIResponseStatusOK) { 202 | completion(response, nil); 203 | } 204 | // If network request was successfull, but Google Places API 205 | // responded with error status code 206 | else { 207 | completion(response, [[self class] ftgp_errorForResponseStatus:response.status]); 208 | } 209 | }]; 210 | } 211 | 212 | + (void)setDebugLoggingEnabled:(BOOL)enabled 213 | { 214 | FTGooglePlacesAPIDebugLoggingEnabled = enabled; 215 | } 216 | 217 | #pragma mark - Private class methods 218 | 219 | + (void)executeRequest:(id)request 220 | withCompletionHandler:(void(^)(NSDictionary *responseObject, NSError *error))completion 221 | { 222 | NSAssert(completion, @"You must provide completion block for the Google Places API request execution. Performing request without handling does not make any sense."); 223 | 224 | // Instance 225 | FTGooglePlacesAPIService *service = [[self class] sharedService]; 226 | 227 | // Check API key 228 | NSString *apiKey = service.apiKey; 229 | NSAssert([apiKey length] > 0, @"You must first provide API key using [FTGooglePlacesAPIService provideAPIKey:] before executing Google Places API requests"); 230 | 231 | NSMutableDictionary *params = [[request placesAPIRequestParams] mutableCopy]; 232 | 233 | // Add params which this service takes care of 234 | params[@"key"] = apiKey; 235 | params[@"sensor"] = @"true"; // Constant for now 236 | 237 | // Create relative request path 238 | // Places API base URL is already configured in AFNetworking HTTP manager 239 | NSString *requestPath = [NSString stringWithFormat:@"%@/json", [request placesAPIRequestMethod]]; 240 | 241 | // Perform request using AFNetworking 242 | AFHTTPRequestOperationManager *manager = service.httpRequestOperationManager; 243 | 244 | // Perform request 245 | [manager GET:requestPath 246 | parameters:params 247 | success:^(AFHTTPRequestOperation *operation, id responseObject) 248 | { 249 | FTGPServiceLog(@"%@ request SUCCESS (Request URL: %@)", [self class], operation.request.URL); 250 | completion(responseObject, nil); 251 | } 252 | failure:^(AFHTTPRequestOperation *operation, NSError *error) 253 | { 254 | FTGPServiceLog(@"%@ request FAILURE: (Request URL: %@, Error: %@)", [self class], operation.request.URL, error); 255 | completion(nil, error); 256 | }]; 257 | } 258 | 259 | @end 260 | 261 | #pragma mark - Private methods category implementation 262 | 263 | @implementation FTGooglePlacesAPIService (Private) 264 | 265 | + (NSError *)ftgp_errorForResponseStatus:(FTGooglePlacesAPIResponseStatus)status 266 | { 267 | NSDictionary *userInfo = @{ 268 | NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Google Places API request failed", nil), 269 | NSLocalizedDescriptionKey: [FTGooglePlacesAPIResponse localizedDescriptionForStatus:status] 270 | }; 271 | 272 | return [NSError errorWithDomain:FTGooglePlacesAPIErrorDomain code:status userInfo:userInfo]; 273 | } 274 | 275 | @end 276 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPITextSearchRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPITextSearchRequest.h 3 | // 4 | // Created by Lukas Kukacka on 10/30/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import 30 | #import 31 | #import "FTGooglePlacesAPICommon.h" 32 | 33 | /** 34 | * Class for encapsulating Google Places API "Text Search" request. 35 | * See https://developers.google.com/places/documentation/search 36 | * 37 | * The Google Places API Text Search Service is a web service that 38 | * returns information about a set of Places based on a string 39 | * — for example "pizza in New York" or "shoe stores near Ottawa". 40 | * The service responds with a list of Places matching the text 41 | * string and any location bias that has been set. 42 | * 43 | * FTGooglePlacesAPINearbySearchRequest and FTGooglePlacesAPITextSearchRequest 44 | * have a lot of the same code. They don't have the common superclass 45 | * (and so have copied code) to make them totaly standalone. 46 | * We don't have any quarantee the single concrete request won't change 47 | * in the future (keys dependency or exclusivity etc.) and shared common 48 | * code and properties could cause a lot of trouble in that case 49 | */ 50 | 51 | @interface FTGooglePlacesAPITextSearchRequest : NSObject 52 | 53 | /** 54 | * The text string on which to search, for example: "restaurant". The Place service 55 | * will return candidate matches based on this string and order the results based 56 | * on their perceived relevance. 57 | * Property is read-only, is set in init method. 58 | */ 59 | @property (nonatomic, copy, readonly) NSString *query; 60 | 61 | /** 62 | * The latitude/longitude around which to retrieve Place information. 63 | * This stands for "location" Google Places API parameter 64 | * This must be specified as latitude,longitude. If you specify a location 65 | * parameter, you must also specify a radius parameter. 66 | */ 67 | @property (nonatomic, assign) CLLocationCoordinate2D locationCoordinate; 68 | 69 | /** 70 | * Defines the distance (in meters) within which to return Place results. 71 | * The maximum allowed radius is 50 000 meters. Default: none 72 | */ 73 | @property (nonatomic, assign) NSUInteger radius; 74 | 75 | /** 76 | * The language code, indicating in which language the results should be returned, if possible. 77 | * See http://spreadsheets.google.com/pub?key=p9pdwsai2hDMsLkXsoM05KQ&gid=1 for a complete list 78 | * 79 | * Default value is determined by active application language from NSUserDefaults 80 | * if available 81 | * (details: https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/Articles/ChoosingLocalizations.html) 82 | */ 83 | @property (nonatomic, copy) NSString *language; 84 | 85 | /** 86 | * Restricts results to only those places within the specified range. Valid values 87 | * range between 0 (most affordable) to 4 (most expensive), inclusive. The exact amount 88 | * indicated by a specific value will vary from region to region. 89 | * When passed in value is >4, 4 is used 90 | * Default value is NSUIntegerMax indicating this value will not be present in a request 91 | */ 92 | @property (nonatomic, assign) NSUInteger minPrice; 93 | @property (nonatomic, assign) NSUInteger maxPrice; 94 | 95 | /** 96 | * Returns only those Places that are open for business at the time 97 | * the query is sent. Places that do not specify opening hours 98 | * in the Google Places database will not be returned if you 99 | * include this parameter in your query. 100 | */ 101 | @property (nonatomic, assign) BOOL openNow; 102 | 103 | /** 104 | * Restricts the results to Places matching at least one of the specified types. 105 | * Provide array of NSStrings 106 | * See https://developers.google.com/places/documentation/supported_types 107 | * for the list of all available types 108 | */ 109 | @property (nonatomic, copy) NSArray *types; 110 | 111 | /** 112 | * Creates new intance of Places API request. 113 | * 114 | * @param The text string on which to search, for example: "restaurant". The Place service will return candidate matches based on this string and order the results based on their perceived relevance. 115 | * 116 | * @return Request instance. If the location provided is invalid, returns nil 117 | */ 118 | - (instancetype)initWithQuery:(NSString *)query; 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /FTGooglePlacesAPI/FTGooglePlacesAPITextSearchRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPITextSearchRequest.m 3 | // 4 | // Created by Lukas Kukacka on 10/30/13. 5 | // 6 | // 7 | // The MIT License (MIT) 8 | // 9 | // Copyright (c) 2013 Fuerte Int. All rights reserved. 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import "FTGooglePlacesAPITextSearchRequest.h" 30 | 31 | static const NSUInteger kMaxRadius = 50000; 32 | 33 | @implementation FTGooglePlacesAPITextSearchRequest 34 | 35 | #pragma Lifecycle 36 | 37 | - (instancetype)initWithQuery:(NSString *)query 38 | { 39 | self = [super init]; 40 | if(self) 41 | { 42 | // Validate query 43 | if ([query length] == 0) { 44 | NSLog(@"WARNING: %s: Search query is empty, returning nil", __PRETTY_FUNCTION__); 45 | return nil; 46 | } 47 | 48 | _query = query; 49 | 50 | // Default values 51 | _radius = kMaxRadius+1; // Indicate "no value" by overflowing max radius value 52 | _locationCoordinate = CLLocationCoordinate2DMake(10000, 10000); // Default is invalid value 53 | _language = [FTGooglePlacesAPIUtils deviceLanguage]; 54 | _minPrice = NSUIntegerMax; 55 | _maxPrice = NSUIntegerMax; 56 | } 57 | return self; 58 | } 59 | 60 | #pragma mark - Accessors 61 | 62 | - (void)setRadius:(NSUInteger)radius 63 | { 64 | [self willChangeValueForKey:@"radius"]; 65 | 66 | // Validate radius 67 | _radius = radius; 68 | if (_radius > kMaxRadius) { 69 | NSLog(@"WARNING: %s: Radius %ldm is too big. Maximum radius is %ldm, using maximum", __PRETTY_FUNCTION__, (unsigned long)radius, (unsigned long)kMaxRadius); 70 | _radius = kMaxRadius; 71 | } 72 | 73 | [self didChangeValueForKey:@"radius"]; 74 | } 75 | 76 | - (void)setMinPrice:(NSUInteger)minPrice 77 | { 78 | [self willChangeValueForKey:@"minPrice"]; 79 | 80 | // value ranges 0-4 81 | _minPrice = MAX(0,MIN(4, minPrice)); 82 | 83 | [self didChangeValueForKey:@"minPrice"]; 84 | } 85 | 86 | - (void)setMaxPrice:(NSUInteger)maxPrice 87 | { 88 | [self willChangeValueForKey:@"maxPrice"]; 89 | 90 | // value ranges 0-4 91 | _maxPrice = MAX(0,MIN(4, maxPrice)); 92 | 93 | [self didChangeValueForKey:@"maxPrice"]; 94 | } 95 | 96 | #pragma mark - Superclass overrides 97 | 98 | - (NSString *)description 99 | { 100 | return [NSString stringWithFormat:@"<%@: %p> %@", [self class], self, [self placesAPIRequestParams]]; 101 | } 102 | 103 | #pragma mark - FTGooglePlacesAPIRequest protocol 104 | 105 | - (NSString *)placesAPIRequestMethod 106 | { 107 | return @"textsearch"; 108 | } 109 | 110 | - (NSDictionary *)placesAPIRequestParams 111 | { 112 | NSMutableDictionary *params = [NSMutableDictionary dictionary]; 113 | 114 | if(_query) { 115 | params[@"query"] = _query; 116 | } 117 | 118 | // Required parameters 119 | if (CLLocationCoordinate2DIsValid(_locationCoordinate)) { 120 | params[@"location"] = [NSString stringWithFormat:@"%.7f,%.7f", _locationCoordinate.latitude, _locationCoordinate.longitude]; 121 | } 122 | 123 | // Radius is optional for text search 124 | if (_radius <= kMaxRadius) { 125 | params[@"radius"] = [NSString stringWithFormat:@"%ld", (unsigned long)_radius]; 126 | } 127 | 128 | // Optional parameters 129 | if (_language) { 130 | params[@"language"] = _language; 131 | }; 132 | 133 | if (_minPrice != NSUIntegerMax) { 134 | params[@"minprice"] = [NSString stringWithFormat:@"%ld", (unsigned long)_minPrice]; 135 | } 136 | 137 | if (_maxPrice != NSUIntegerMax) { 138 | params[@"maxprice"] = [NSString stringWithFormat:@"%ld", (unsigned long)_maxPrice]; 139 | } 140 | 141 | if (_openNow) { 142 | params[@"opennow"] = [NSNull null]; 143 | } 144 | 145 | if ([_types count] > 0) { 146 | params[@"types"] = [_types componentsJoinedByString:@"|"]; 147 | } 148 | 149 | return [params copy]; 150 | } 151 | 152 | @end 153 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 Fuerte International 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #FTGooglePlacesAPI 2 | 3 | An open-source iOS Objective-C library for querying [Google Places API][1] using simple callback-blocks based interface. 4 | 5 | Perform Google Places API requests with ease in a few lines of code. Library includes, but is not limited to, following: 6 | 7 | - [Place Search][2] 8 | - Nearby Search (search places withing a specified area) 9 | - Text Search (search places based on a search string) 10 | - [Place Details][3] (get more comprehensive information about a place) 11 | 12 | 13 | 14 | 19 | 24 | 29 | 30 |
15 | 16 | FTGooglePlacesAPI example screenshot 17 | 18 | 20 | 21 | FTGooglePlacesAPI example screenshot 22 | 23 | 25 | 26 | FTGooglePlacesAPI example screenshot 27 | 28 |
31 | 32 | ##Instalation 33 | 34 | ### CocoaPods 35 | 36 | *FTGooglePlacesAPI* is available as a CocoaPod 37 | 38 | pod 'FTGooglePlacesAPI' 39 | 40 | ### Manual (or using git submodule) 41 | 1. Implement [AFNetworking 2.0][4] 42 | - FTGooglePlacesAPI uses AFNetworking 2.0 for all of its networking. Why? Because it rocks! 43 | 2. Download source files from this repository 44 | - Or use GIT submodule ( `git submodule add git@github.com:FuerteInternational/FTGooglePlacesAPI.git` ) 45 | 3. Add all files from *FTGooglePlacesAPI* folder to your project's target 46 | 47 | ##Usage 48 | 49 | You can take a look at the detailed example usage project *XcodeProject/FTGooglePlacesAPI.xcodeproj*. Just be sure to provide your own API key. Or dive in yourself following these few steps... 50 | 51 | #### 1. Import FTGooglePlacesAPI files in your implementation file 52 | ```objective-c 53 | #import "FTGooglePlacesAPI.h" 54 | ``` 55 | 56 | #### 2. Setup service 57 | 58 | In order to communicate with a Google Places API, you must first generate your own API key which you can get at [Google Play Developer Console][5]. You can also take a look at [Introduction - Google Places API][6]. 59 | 60 | You must provide API key to a `FTGooglePlacesAPI` service before making any request using it. Good place for this code is the App Delegate. 61 | 62 | ```objective-c 63 | // Provide API key to FTGooglePlacesAPIService before making any requests 64 | [FTGooglePlacesAPIService provideAPIKey:@"YOUR_API_KEY"]; 65 | ``` 66 | 67 | Optionaly, you can enable debug logging. If this is set to `YES` and when building in debug mode (`#ifdef DEBUG`), service will print some debugging information to the console. In fact there is no need to remove this code even from Release build since the service will not produce any output in non-debug builds. 68 | 69 | ```objective-c 70 | // Optionally enable debug mode 71 | [FTGooglePlacesAPIService setDebugLoggingEnabled:YES]; 72 | ``` 73 | 74 | #### 3. Create a request 75 | 76 | Construct a desired request. 77 | 78 | ```objective-c 79 | // Create location around which to search (hardcoded location of Big Ben here) 80 | CLLocationCoordinate2D locationCoordinate = CLLocationCoordinate2DMake(51.501103,-0.124565); 81 | 82 | // Create request searching nearest galleries and museums 83 | FTGooglePlacesAPINearbySearchRequest *request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:locationCoordinate]; 84 | request.rankBy = FTGooglePlacesAPIRequestParamRankByDistance; 85 | request.types = @[@"art_gallery", @"museum"]; 86 | ``` 87 | 88 | There is a lot of possibilities since the implemented requests objects supports all of the API's functionality. See the example project and the [Google Places API documentation][7]. 89 | 90 | **Tip**: You can determine user's current location very easily using [FTLocationManager][8]. 91 | 92 | #### 4. Perform request and handle the results 93 | 94 | ```objective-c 95 | // Execute Google Places API request using FTGooglePlacesAPIService 96 | [FTGooglePlacesAPIService executeSearchRequest:request 97 | withCompletionHandler:^(FTGooglePlacesAPISearchResponse *response, NSError *error) { 98 | 99 | // If error is not nil, request failed and you should handle the error 100 | if (error) 101 | { 102 | // Handle error here 103 | NSLog(@"Request failed. Error: %@", error); 104 | 105 | // There may be a lot of causes for an error (for example networking error). 106 | // If the network communication with Google Places API was successfull, 107 | // but the API returned some non-ok status code, NSError will have 108 | // FTGooglePlacesAPIErrorDomain domain and status code from 109 | // FTGooglePlacesAPIResponseStatus enum 110 | // You can inspect error's domain and status code for more detailed info 111 | } 112 | 113 | // Everything went fine, we have response object we can process 114 | NSLog(@"Request succeeded. Response: %@", response); 115 | }]; 116 | ``` 117 | 118 | You must use the proper method based on a request type you want to perform because the service will construct the response objects based on a method being called. 119 | 120 | Available methods are: 121 | 122 | - `+ (void)executeSearchRequest:withCompletionHandler:` for a both *Nearest* and *Text Search* requests 123 | - `+ (void)executeDetailRequest:withCompletionHandler:` for a *Place Detail* request 124 | 125 | ##Compatibility 126 | 127 | - iOS 6+ 128 | - Mainly because of a dependency on AFNetworking 2.0 (although it shouldn't be difficult for you to remove dependency on it) 129 | - ARC 130 | 131 | ##Contact 132 | 133 | FTGooglePlacesAPI is developed by [Fuerte International](http://fuerteint.com). Please [drop us an email](mailto:open-source@fuerteint.com) to let us know you how you are using this component. 134 | 135 | ##Contributing and notes 136 | 137 | - If you like the library, please consider giving it a Github star to let us know it. 138 | - All header files are heavily commented in format compatible with Xcode 5 quick docs preview (Option + Click) 139 | - Library is unit tested using Apple's *XCTest* and [OCMock](http://ocmock.org/) 140 | 141 | Pull requests are very welcome expecting you follow few rules: 142 | 143 | - Document your changes in a code comments and Git commit message 144 | - Make sure your changes didn't cause any trouble using included example project, unit tests and if appropriate, implement unit tests and example code for your newly added functionality 145 | 146 | ##Version history 147 | 148 | #### 1.1 149 | - Implemented deprecations for usage of `id` and `reference` properties as these has been deprecated by Google as of June 24, 2014. See [Deprecation notice in documentation](https://developers.google.com/places/documentation/details#deprecation) for more info. 150 | 151 | #### 1.0 152 | - First official public release 153 | 154 | ##License 155 | ``` 156 | The MIT License (MIT) 157 | 158 | Copyright (c) 2013-2014 Fuerte International 159 | 160 | Permission is hereby granted, free of charge, to any person obtaining a copy 161 | of this software and associated documentation files (the "Software"), to deal 162 | in the Software without restriction, including without limitation the rights 163 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 164 | copies of the Software, and to permit persons to whom the Software is 165 | furnished to do so, subject to the following conditions: 166 | 167 | The above copyright notice and this permission notice shall be included in 168 | all copies or substantial portions of the Software. 169 | 170 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 171 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 172 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 173 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 174 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 175 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 176 | THE SOFTWARE. 177 | 178 | ``` 179 | 180 | 181 | [1]: https://developers.google.com/places/documentation/ 182 | [2]: https://developers.google.com/places/documentation/search 183 | [3]: https://developers.google.com/places/documentation/details 184 | [4]: https://github.com/AFNetworking/AFNetworking 185 | [5]: https://code.google.com/apis/console 186 | [6]: https://developers.google.com/places/documentation/#Authentication 187 | [7]: https://developers.google.com/places/documentation 188 | [8]: https://github.com/FuerteInternational/FTLocationManager -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI.xcodeproj/xcshareddata/xcschemes/Demo-FTGooglePlacesAPI.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI.xcodeproj/xcshareddata/xcschemes/FTGooglePlacesAPILogicTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 49 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTAppDelegate.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 19/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FTAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTAppDelegate.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 19/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTAppDelegate.h" 10 | 11 | #import "FTGooglePlacesAPIExamplesListViewController.h" 12 | 13 | #import "FTGooglePlacesAPI.h" 14 | 15 | @implementation FTAppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 20 | 21 | UIViewController *rootController = [[FTGooglePlacesAPIExamplesListViewController alloc] init]; 22 | UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:rootController]; 23 | 24 | #warning Possible incomplete implementation. Make sure you add you own Google Places API key 25 | [FTGooglePlacesAPIService provideAPIKey:@"<#PLACE YOUR API KEY HERE#>"]; 26 | 27 | self.window.rootViewController = navController; 28 | 29 | [self createAttributionsViews]; 30 | [self.window makeKeyAndVisible]; 31 | 32 | return YES; 33 | } 34 | 35 | - (void)applicationWillResignActive:(UIApplication *)application 36 | { 37 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 38 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 39 | } 40 | 41 | - (void)applicationDidEnterBackground:(UIApplication *)application 42 | { 43 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 44 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 45 | } 46 | 47 | - (void)applicationWillEnterForeground:(UIApplication *)application 48 | { 49 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 50 | } 51 | 52 | - (void)applicationDidBecomeActive:(UIApplication *)application 53 | { 54 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 55 | } 56 | 57 | - (void)applicationWillTerminate:(UIApplication *)application 58 | { 59 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 60 | } 61 | 62 | #pragma mark - Helper methods 63 | 64 | - (void)createAttributionsViews 65 | { 66 | UIView *containerView = self.window.rootViewController.view; 67 | 68 | // To comply with Google Places API Policies at https://developers.google.com/places/policies 69 | // we display "Powered by Google" logo. For simplicity, it is simply added as a subview 70 | // to root controller's view. This ensures the logo is always visible. 71 | UIImage *googleLogo = [UIImage imageNamed:@"powered-by-google-on-white"]; 72 | UIImageView *googleLogoView = [[UIImageView alloc] initWithImage:googleLogo]; 73 | googleLogoView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin; 74 | googleLogoView.userInteractionEnabled = NO; 75 | googleLogoView.opaque = NO; 76 | 77 | CGRect googleLogoFrame = googleLogoView.frame; 78 | googleLogoFrame.origin = CGPointMake(10.0f, 79 | CGRectGetHeight(containerView.frame) - googleLogo.size.height - 10.0f); 80 | googleLogoView.frame = googleLogoFrame; 81 | 82 | [containerView addSubview:googleLogoView]; 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTGooglePlacesAPI-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.acme.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTGooglePlacesAPI-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTGooglePlacesAPIExampleDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIExampleDetailViewController.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 29/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol FTGooglePlacesAPIRequest; 12 | 13 | @interface FTGooglePlacesAPIExampleDetailViewController : UITableViewController 14 | 15 | - (instancetype)initWithRequest:(id)request; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTGooglePlacesAPIExampleDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIExampleDetailViewController.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 29/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPIExampleDetailViewController.h" 10 | 11 | #import "FTGooglePlacesAPI.h" 12 | 13 | @interface FTGooglePlacesAPIExampleDetailViewController () 14 | 15 | @property (nonatomic, strong) id request; 16 | @property (nonatomic, strong) FTGooglePlacesAPIDetailResponse *response; 17 | 18 | @property (nonatomic, strong) NSDictionary *responseTableRepresentation; 19 | 20 | @property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView; 21 | 22 | @end 23 | 24 | @implementation FTGooglePlacesAPIExampleDetailViewController 25 | 26 | - (instancetype)initWithRequest:(id)request 27 | { 28 | self = [super initWithStyle:UITableViewStylePlain]; 29 | if (self) { 30 | self.title = @"Detail"; 31 | _request = request; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)viewDidLoad 37 | { 38 | [super viewDidLoad]; 39 | 40 | self.activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 41 | _activityIndicatorView.hidesWhenStopped = YES; 42 | 43 | UIBarButtonItem *activityBarButton = [[UIBarButtonItem alloc] initWithCustomView:_activityIndicatorView]; 44 | self.navigationItem.rightBarButtonItem = activityBarButton; 45 | } 46 | 47 | - (void)viewWillAppear:(BOOL)animated 48 | { 49 | [super viewWillAppear:animated]; 50 | 51 | [self startSearching]; 52 | } 53 | 54 | #pragma mark - Table view data source 55 | 56 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 57 | { 58 | return 1; 59 | } 60 | 61 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 62 | { 63 | return [_responseTableRepresentation count]; 64 | } 65 | 66 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 67 | { 68 | static NSString *CellIdentifier = @"Cell"; 69 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 70 | if (!cell) { 71 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier]; 72 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 73 | } 74 | 75 | NSString *key = [_responseTableRepresentation allKeys][indexPath.row]; 76 | 77 | cell.textLabel.text = key; 78 | cell.detailTextLabel.text = [_responseTableRepresentation[key] description]; 79 | 80 | return cell; 81 | } 82 | 83 | #pragma mark - FTGooglePlacesAPI performing search request 84 | 85 | - (void)startSearching 86 | { 87 | // Show activity indicator 88 | [_activityIndicatorView startAnimating]; 89 | 90 | 91 | // Execute Google Places API request using FTGooglePlacesAPIService 92 | [FTGooglePlacesAPIService executeDetailRequest:_request 93 | withCompletionHandler:^(FTGooglePlacesAPIDetailResponse *response, NSError *error) 94 | { 95 | // If error is not nil, request failed and you should handle the error 96 | // We just show alert 97 | if (error) 98 | { 99 | // There may be a lot of causes for an error (for example networking error). 100 | // If the network communication with Google Places API was successfull, 101 | // but the API returned some status code, NSError will have 102 | // FTGooglePlacesAPIErrorDomain domain and status code from 103 | // FTGooglePlacesAPIResponseStatus enum 104 | // You can inspect error's domain and status code for more detailed info 105 | 106 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[error localizedDescription] message:[error localizedFailureReason] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; 107 | [alert show]; 108 | [_activityIndicatorView stopAnimating]; 109 | return; 110 | } 111 | 112 | // Everything went fine, we have response object 113 | // You can do whatever you need here, we just construct "table representation" 114 | // of response to the dictionary and reload table 115 | _responseTableRepresentation = @{ 116 | @"Name": (response.name?:@"N/A"), 117 | @"Location": [NSString stringWithFormat:@"(%.6f,%.6f)", response.location.coordinate.latitude, response.location.coordinate.longitude], 118 | @"Address": (response.addressString?:@"N/A"), 119 | @"Form. address": (response.formattedAddress?:@"N/A"), 120 | @"Form. phone": (response.formattedPhoneNumber?:@"N/A"), 121 | @"Int. phone": (response.internationalPhoneNumber?:@"N/A"), 122 | @"Rating": [NSString stringWithFormat:@"%.1f", response.rating], 123 | @"Types": [response.types componentsJoinedByString:@","], 124 | @"Website:": (response.websiteUrl?:@"N/A"), 125 | }; 126 | 127 | [self.tableView reloadData]; 128 | 129 | [_activityIndicatorView stopAnimating]; 130 | }]; 131 | 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTGooglePlacesAPIExampleResultsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIExampleResultsViewController.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 20/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol FTGooglePlacesAPIRequest; 13 | 14 | /** 15 | * Please note that this controller is not 100% functional and glitches-proof 16 | * We try to keep the code as simple as possible so some cases which should be 17 | * handled in production code are not handled here 18 | */ 19 | 20 | @interface FTGooglePlacesAPIExampleResultsViewController : UITableViewController 21 | 22 | /** 23 | * @param request This is initial request used for the first request 24 | * @param locationCoordinate Location from which the distance should be shown 25 | * 26 | * @return Instance 27 | */ 28 | - (id)initWithGooglePlacesAPIRequest:(id)request locationCoordinate:(CLLocationCoordinate2D)locationCoordinate; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTGooglePlacesAPIExampleResultsViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIExampleResultsViewController.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 20/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPIExampleResultsViewController.h" 10 | 11 | #import "FTGooglePlacesAPI.h" 12 | #import "UIImageView+AFNetworking.h" 13 | 14 | #import "FTGooglePlacesAPIExampleDetailViewController.h" 15 | 16 | 17 | @interface FTGooglePlacesAPIExampleResultsViewController () 18 | 19 | @property (nonatomic, strong) id initialRequest; 20 | @property (nonatomic, strong) id actualRequest; 21 | @property (nonatomic, strong) CLLocation *searchLocation; 22 | @property (nonatomic, strong) FTGooglePlacesAPISearchResponse *lastResponse; 23 | 24 | @property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView; 25 | 26 | /** 27 | * This array holds all results. Mutable array is used because we can have 28 | * multiple responses, but want to keep all result items 29 | */ 30 | @property (nonatomic, strong) NSMutableArray *results; 31 | 32 | @end 33 | 34 | @implementation FTGooglePlacesAPIExampleResultsViewController 35 | 36 | - (id)initWithGooglePlacesAPIRequest:(id)request locationCoordinate:(CLLocationCoordinate2D)locationCoordinate; 37 | { 38 | NSAssert(request, @"Provided request cannot be nil"); 39 | 40 | self = [super initWithStyle:UITableViewStylePlain]; 41 | if (self) 42 | { 43 | self.title = @"Results"; 44 | 45 | _initialRequest = request; 46 | _actualRequest = request; 47 | 48 | _results = [NSMutableArray array]; 49 | 50 | // Create CLLocation object from search coordinate. We will use this for calculating 51 | // distance of result item 52 | _searchLocation = [[CLLocation alloc] initWithLatitude:locationCoordinate.latitude longitude:locationCoordinate.longitude]; 53 | } 54 | return self; 55 | } 56 | 57 | - (void)viewDidLoad 58 | { 59 | [super viewDidLoad]; 60 | 61 | self.activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 62 | _activityIndicatorView.hidesWhenStopped = YES; 63 | 64 | UIBarButtonItem *activityBarButton = [[UIBarButtonItem alloc] initWithCustomView:_activityIndicatorView]; 65 | self.navigationItem.rightBarButtonItem = activityBarButton; 66 | } 67 | 68 | - (void)viewWillAppear:(BOOL)animated 69 | { 70 | [super viewWillAppear:animated]; 71 | 72 | // Controller is first displayed - start searching 73 | if ([_results count] == 0) { 74 | [self startSearching]; 75 | } 76 | } 77 | 78 | #pragma mark Table view data source 79 | 80 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 81 | { 82 | return 1; 83 | } 84 | 85 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 86 | { 87 | return [_results count] + ([_lastResponse hasNextPage]); 88 | } 89 | 90 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 91 | { 92 | static NSString *ResultCellIdentifier = @"ResultCell"; 93 | static NSString *LoadMoreCellIdentifier = @"LoadMoreCell"; 94 | 95 | BOOL isLoadMoreResultsCell = [self isLoadMoreResultsCellAtIndexPath:indexPath]; 96 | 97 | // Get appropriate cell 98 | NSString *cellIdentifier = (isLoadMoreResultsCell? LoadMoreCellIdentifier:ResultCellIdentifier); 99 | 100 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 101 | 102 | // Configure "Load more" cell 103 | if (isLoadMoreResultsCell) 104 | { 105 | // This is constant cell, so we can preconfigure it on the init 106 | if (!cell) { 107 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 108 | cell.textLabel.textAlignment = NSTextAlignmentCenter; 109 | cell.textLabel.text = @"Load more results..."; 110 | } 111 | } 112 | // Configure results cell 113 | else 114 | { 115 | if (!cell) { 116 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier]; 117 | cell.imageView.contentMode = UIViewContentModeScaleAspectFit; 118 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 119 | } 120 | 121 | // Get response object 122 | FTGooglePlacesAPISearchResultItem *resultItem = _results[indexPath.row]; 123 | 124 | 125 | // Configure cell 126 | cell.textLabel.text = resultItem.name; 127 | 128 | if (resultItem.location) { 129 | CLLocationDistance distance = [resultItem.location distanceFromLocation:_searchLocation]; 130 | cell.detailTextLabel.text = [NSString stringWithFormat:@"Distance: %.0fm", distance]; 131 | } 132 | 133 | [cell.imageView setImageWithURL:[NSURL URLWithString:resultItem.iconImageUrl] placeholderImage:[self placeholderImage]]; 134 | } 135 | 136 | return cell; 137 | } 138 | 139 | #pragma mark Table view delegate 140 | 141 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 142 | { 143 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 144 | 145 | // Selected "Load more results" cell 146 | if ([self isLoadMoreResultsCellAtIndexPath:indexPath]) 147 | { 148 | // Get request for a new page of results and start search 149 | id nextPageRequest = [_lastResponse nextPageRequest]; 150 | _actualRequest = nextPageRequest; 151 | [self startSearching]; 152 | } 153 | // Selected result item cell 154 | else 155 | { 156 | // Get response object 157 | FTGooglePlacesAPISearchResultItem *resultItem = _results[indexPath.row]; 158 | 159 | // Create detail request 160 | FTGooglePlacesAPIDetailRequest *request = [[FTGooglePlacesAPIDetailRequest alloc] initWithPlaceId:resultItem.placeId]; 161 | 162 | // Create detail controller 163 | FTGooglePlacesAPIExampleDetailViewController *detailController = [[FTGooglePlacesAPIExampleDetailViewController alloc] initWithRequest:request]; 164 | 165 | [self.navigationController pushViewController:detailController animated:YES]; 166 | 167 | // And print it to the console 168 | NSLog(@"Selected item: %@", resultItem); 169 | } 170 | } 171 | 172 | #pragma mark - Helper methods 173 | 174 | - (BOOL)isLoadMoreResultsCellAtIndexPath:(NSIndexPath *)indexPath 175 | { 176 | // It is load more cell if there is more results to read and this is 177 | // the last cell in a table view 178 | NSInteger numberOfRows = [self tableView:self.tableView numberOfRowsInSection:indexPath.section]; 179 | 180 | return ((indexPath.row == numberOfRows - 1) && [_lastResponse hasNextPage]); 181 | } 182 | 183 | /** 184 | * Little helper for generating plain white image used as a placeholder 185 | * in UITableViewCell's while loading icon images 186 | */ 187 | - (UIImage *)placeholderImage 188 | { 189 | static UIImage *PlaceholderImage; 190 | 191 | if (!PlaceholderImage) 192 | { 193 | CGRect rect = CGRectMake(0, 0, 40.0f, 40.0f); 194 | 195 | UIGraphicsBeginImageContext(rect.size); 196 | [[UIColor whiteColor] setFill]; 197 | [[UIBezierPath bezierPathWithRect:rect] fill]; 198 | PlaceholderImage = UIGraphicsGetImageFromCurrentImageContext(); 199 | UIGraphicsEndImageContext(); 200 | } 201 | 202 | return PlaceholderImage; 203 | } 204 | 205 | #pragma mark - FTGooglePlacesAPI performing search request 206 | 207 | - (void)startSearching 208 | { 209 | // Show activity indicator 210 | [_activityIndicatorView startAnimating]; 211 | 212 | 213 | // Execute Google Places API request using FTGooglePlacesAPIService 214 | [FTGooglePlacesAPIService executeSearchRequest:_actualRequest 215 | withCompletionHandler:^(FTGooglePlacesAPISearchResponse *response, NSError *error) 216 | { 217 | // If error is not nil, request failed and you should handle the error 218 | // We just show alert 219 | if (error) 220 | { 221 | // There may be a lot of causes for an error (for example networking error). 222 | // If the network communication with Google Places API was successfull, 223 | // but the API returned some status code, NSError will have 224 | // FTGooglePlacesAPIErrorDomain domain and status code from 225 | // FTGooglePlacesAPIResponseStatus enum 226 | // You can inspect error's domain and status code for more detailed info 227 | 228 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[error localizedDescription] message:[error localizedFailureReason] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; 229 | [alert show]; 230 | [_activityIndicatorView stopAnimating]; 231 | return; 232 | } 233 | 234 | // Everything went fine, we have response object 235 | // You can do whatever you need here, we just add new items to the 236 | // data source array and reload the table 237 | // You could add new rows with animation etc., but it would add useless 238 | // complexity to the sample code app 239 | 240 | // Update last response object 241 | _lastResponse = response; 242 | 243 | // Add new results to the data source array 244 | [_results addObjectsFromArray:response.results]; 245 | 246 | [self.tableView reloadData]; 247 | 248 | [_activityIndicatorView stopAnimating]; 249 | }]; 250 | 251 | } 252 | 253 | @end 254 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTGooglePlacesAPIExamplesListViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIExamplesListViewController.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 20/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FTGooglePlacesAPIExamplesListViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/FTGooglePlacesAPIExamplesListViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIExamplesListViewController.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 20/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPIExamplesListViewController.h" 10 | 11 | #import "FTGooglePlacesAPI.h" 12 | #import "FTGooglePlacesAPIExampleResultsViewController.h" 13 | 14 | typedef NS_ENUM(NSUInteger, FTGooglePlacesAPIExampleType) { 15 | FTGooglePlacesAPIExampleTypeNearestCulture, 16 | FTGooglePlacesAPIExampleTypeMuseumKeyword, 17 | FTGooglePlacesAPIExampleTypeTextSearchPizzaInLondon, 18 | FTGooglePlacesAPIExampleTypeExpensiveRestaurant 19 | }; 20 | 21 | 22 | @interface FTGooglePlacesAPIExamplesListViewController () 23 | 24 | @property (nonatomic, assign) CLLocationCoordinate2D locationCoordinate; 25 | 26 | @end 27 | 28 | @implementation FTGooglePlacesAPIExamplesListViewController 29 | 30 | - (id)initWithStyle:(UITableViewStyle)style 31 | { 32 | self = [super initWithStyle:style]; 33 | if (self) { 34 | self.title = @"FTGooglePlacesAPI"; 35 | 36 | // For sake of simplicity of the example, we do not get real location, which would require 37 | // additional code and just use constant latitude and longitude of London's Big Ben 38 | // For easy to use getting of current location, you take a look at FTLocationManager 39 | // https://github.com/FuerteInternational/FTLocationManager 40 | self.locationCoordinate = CLLocationCoordinate2DMake(51.501103,-0.124565); 41 | } 42 | return self; 43 | } 44 | 45 | #pragma mark Table view data source 46 | 47 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 48 | { 49 | return 1; 50 | } 51 | 52 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 53 | { 54 | return 4; // Number of items in FTGooglePlacesAPIExampleType enum 55 | } 56 | 57 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 58 | { 59 | static NSString *CellIdentifier = @"ExampleCell"; 60 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 61 | if(!cell) { 62 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; 63 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 64 | cell.textLabel.adjustsFontSizeToFitWidth = YES; 65 | cell.textLabel.adjustsFontSizeToFitWidth = YES; 66 | } 67 | 68 | FTGooglePlacesAPIExampleType exampleType = (FTGooglePlacesAPIExampleType)indexPath.row; 69 | 70 | cell.textLabel.text = [self titleForExampleType:exampleType]; 71 | cell.detailTextLabel.text = [self subtitleForExampleType:exampleType]; 72 | 73 | return cell; 74 | } 75 | 76 | #pragma mark Table view delegate 77 | 78 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 79 | { 80 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 81 | 82 | // Get request for current row 83 | FTGooglePlacesAPIExampleType exampleType = (FTGooglePlacesAPIExampleType)indexPath.row; 84 | id request = [self googlePlacesAPIRequestForExampleType:exampleType]; 85 | 86 | // Create search / results controller and push it to the navigation controller 87 | FTGooglePlacesAPIExampleResultsViewController *controller = [[FTGooglePlacesAPIExampleResultsViewController alloc] initWithGooglePlacesAPIRequest:request locationCoordinate:self.locationCoordinate]; 88 | 89 | [self.navigationController pushViewController:controller animated:YES]; 90 | 91 | } 92 | 93 | #pragma mark Helpers and convenience methods 94 | /** 95 | * Below are helper method for getting title and subtitle for cells for each 96 | * example type. Using switch helps us not to forget to implement any 97 | * example item by compiler warnings 98 | */ 99 | - (NSString *)titleForExampleType:(FTGooglePlacesAPIExampleType)type 100 | { 101 | NSString *title; 102 | 103 | switch (type) 104 | { 105 | case FTGooglePlacesAPIExampleTypeNearestCulture: 106 | title = @"Nearest galleries and museums"; 107 | break; 108 | case FTGooglePlacesAPIExampleTypeMuseumKeyword: 109 | title = @"Matching \"museum\", near and open"; 110 | break; 111 | case FTGooglePlacesAPIExampleTypeTextSearchPizzaInLondon: 112 | title = @"Text search \"pizza in london\""; 113 | break; 114 | case FTGooglePlacesAPIExampleTypeExpensiveRestaurant: 115 | title = @"Nearest very expensive restaurants"; 116 | break; 117 | } 118 | 119 | return title; 120 | } 121 | 122 | - (NSString *)subtitleForExampleType:(FTGooglePlacesAPIExampleType)type 123 | { 124 | NSString *subtitle; 125 | 126 | switch (type) 127 | { 128 | case FTGooglePlacesAPIExampleTypeNearestCulture: 129 | subtitle = @"rankBy=distance&types=art_gallery|museum"; 130 | break; 131 | case FTGooglePlacesAPIExampleTypeMuseumKeyword: 132 | subtitle = @"keyword=museum&opennow=true&radius=2000"; 133 | break; 134 | case FTGooglePlacesAPIExampleTypeTextSearchPizzaInLondon: 135 | subtitle = @"query=pizza+in+london"; 136 | break; 137 | case FTGooglePlacesAPIExampleTypeExpensiveRestaurant: 138 | subtitle = @"types=restaurant&minprice=4"; 139 | break; 140 | } 141 | 142 | return subtitle; 143 | } 144 | 145 | #pragma mark - Creating Google Places API requests objects 146 | 147 | /** 148 | * This method constructs request for each API example type 149 | * 150 | * @param type Example type 151 | * 152 | * @return Request object conforming to protocol, which can be used in API service 153 | */ 154 | - (id)googlePlacesAPIRequestForExampleType:(FTGooglePlacesAPIExampleType)type 155 | { 156 | id result; 157 | 158 | 159 | switch (type) 160 | { 161 | /** 162 | * Create request for searching for some culture around 163 | */ 164 | case FTGooglePlacesAPIExampleTypeNearestCulture: 165 | { 166 | FTGooglePlacesAPINearbySearchRequest *request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:self.locationCoordinate]; 167 | request.rankBy = FTGooglePlacesAPIRequestParamRankByDistance; 168 | request.types = @[@"art_gallery", @"museum"]; 169 | 170 | result = request; 171 | } 172 | break; 173 | 174 | /** 175 | * Create request for searching places around *best matching* 176 | * word "museum" and which are open right now and max 2km away 177 | * Because we are using *best matching* and default "rankBy" by 178 | * "prominence" (importance), don't be surprised that results may 179 | * not appear ordered by distance 180 | */ 181 | case FTGooglePlacesAPIExampleTypeMuseumKeyword: 182 | { 183 | FTGooglePlacesAPINearbySearchRequest *request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:self.locationCoordinate]; 184 | request.keyword = @"museum"; 185 | request.openNow = YES; 186 | request.radius = 2000; 187 | 188 | result = request; 189 | } 190 | break; 191 | 192 | /** 193 | * This is example of Google Places API "Text Search Request" 194 | * We are making search for phrase "pizza in london" without 195 | * providing any location around which to search. 196 | * Note that in results screen, there will still be displayed 197 | * distance from Big Ben to examples structure simple 198 | */ 199 | case FTGooglePlacesAPIExampleTypeTextSearchPizzaInLondon: 200 | { 201 | FTGooglePlacesAPITextSearchRequest *request = [[FTGooglePlacesAPITextSearchRequest alloc] initWithQuery:@"pizza in london"]; 202 | 203 | result = request; 204 | } 205 | break; 206 | 207 | /** 208 | * This is an example of request defining results price. 209 | * You can use minPrice and maxPrice for example for searching 210 | * free museums, expensive restaurants etc. 211 | */ 212 | case FTGooglePlacesAPIExampleTypeExpensiveRestaurant: 213 | { 214 | FTGooglePlacesAPINearbySearchRequest *request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:self.locationCoordinate]; 215 | request.types = @[@"restaurant"]; 216 | request.minPrice = 4; 217 | 218 | result = request; 219 | } 220 | } 221 | 222 | return result; 223 | } 224 | 225 | @end 226 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/Resources/Images/Splashscreen/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manGoweb/FTGooglePlacesAPI/b35b1d15121694df75249080e44621722f98d937/XcodeProject/FTGooglePlacesAPI/Resources/Images/Splashscreen/Default-568h@2x.png -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/Resources/Images/Splashscreen/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manGoweb/FTGooglePlacesAPI/b35b1d15121694df75249080e44621722f98d937/XcodeProject/FTGooglePlacesAPI/Resources/Images/Splashscreen/Default.png -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/Resources/Images/Splashscreen/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manGoweb/FTGooglePlacesAPI/b35b1d15121694df75249080e44621722f98d937/XcodeProject/FTGooglePlacesAPI/Resources/Images/Splashscreen/Default@2x.png -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/Resources/Images/powered-by-google-on-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manGoweb/FTGooglePlacesAPI/b35b1d15121694df75249080e44621722f98d937/XcodeProject/FTGooglePlacesAPI/Resources/Images/powered-by-google-on-white.png -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/Resources/Images/powered-by-google-on-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manGoweb/FTGooglePlacesAPI/b35b1d15121694df75249080e44621722f98d937/XcodeProject/FTGooglePlacesAPI/Resources/Images/powered-by-google-on-white@2x.png -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPI/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 19/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "FTAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([FTAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/FTGooglePlacesAPILogicTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.acme.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/FTGooglePlacesAPIDictionaryRequestTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIDictionaryRequestTest.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 21/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPITestCase.h" 10 | 11 | #import "FTGooglePlacesAPIDictionaryRequest.h" 12 | 13 | @interface FTGooglePlacesAPIDictionaryRequestTest : FTGooglePlacesAPITestCase 14 | 15 | @end 16 | 17 | @implementation FTGooglePlacesAPIDictionaryRequestTest 18 | 19 | - (void)testBasicInitialization 20 | { 21 | FTGooglePlacesAPIDictionaryRequest *request; 22 | 23 | request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:nil requestType:nil]; 24 | XCTAssertNil(request, @"request should be nil"); 25 | 26 | request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:@{} requestType:nil]; 27 | XCTAssertNil(request, @"request should be nil"); 28 | 29 | request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:nil requestType:@""]; 30 | XCTAssertNil(request, @"request should be nil"); 31 | 32 | request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:@{} requestType:@""]; 33 | XCTAssertNil(request, @"request should be nil"); 34 | 35 | request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:@{@"testkey": @"testvalue"} requestType:nil]; 36 | XCTAssertNil(request, @"request should be nil"); 37 | 38 | request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:@{@"testkey": @"testvalue"} requestType:@""]; 39 | XCTAssertNil(request, @"request should be nil"); 40 | 41 | request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:@{} requestType:@"testrequest"]; 42 | XCTAssertNotNil(request, @"request should not be nil"); 43 | } 44 | 45 | - (void)testProtocolImplementation 46 | { 47 | FTGooglePlacesAPIDictionaryRequest *request; 48 | 49 | request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:@{@"testkey": @"testvalue"} requestType:@"testrequest"]; 50 | 51 | XCTAssertEqualObjects([request placesAPIRequestMethod], @"testrequest", @"requestTypeUrlString is wrong"); 52 | XCTAssertEqualObjects([request placesAPIRequestParams], @{@"testkey": @"testvalue"}, @"placesAPIRequestParams is wrong"); 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/FTGooglePlacesAPINearbySearchRequestTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPINearbySearchRequestTests.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 21/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPITestCase.h" 10 | 11 | #import "FTGooglePlacesAPINearbySearchRequest.h" 12 | 13 | @interface FTGooglePlacesAPINearbySearchRequestTests : FTGooglePlacesAPITestCase { 14 | 15 | CLLocationCoordinate2D _locationCoordinate; 16 | } 17 | 18 | @end 19 | 20 | @implementation FTGooglePlacesAPINearbySearchRequestTests 21 | 22 | - (void)setUp 23 | { 24 | [super setUp]; 25 | 26 | _locationCoordinate = CLLocationCoordinate2DMake(51.501103,-0.124565); 27 | } 28 | 29 | - (void)testBasicInitialization 30 | { 31 | FTGooglePlacesAPINearbySearchRequest *request; 32 | 33 | request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:_locationCoordinate]; 34 | XCTAssertNotNil(request, @"request should not be nil"); 35 | 36 | CLLocationCoordinate2D invalidLocationCoordinate = CLLocationCoordinate2DMake(10000, 10000); 37 | request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:invalidLocationCoordinate]; 38 | XCTAssertNil(request, @"request should be nil"); 39 | } 40 | 41 | - (void)testProperties 42 | { 43 | FTGooglePlacesAPINearbySearchRequest *request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:_locationCoordinate]; 44 | 45 | XCTAssertEqual(request.locationCoordinate.latitude, _locationCoordinate.latitude, @"locationCoordinate is wrong"); 46 | XCTAssertEqual(request.locationCoordinate.longitude, _locationCoordinate.longitude, @"locationCoordinate is wrong"); 47 | 48 | // Radius 49 | XCTAssertEqual(request.radius, (NSUInteger)5000, @"default radius is wrong"); 50 | request.radius = 12345; 51 | XCTAssertEqual(request.radius, (NSUInteger)12345, @"radius is wrong"); 52 | 53 | // Keyword 54 | XCTAssertNil(request.keyword, @"default keyword is wrong"); 55 | request.keyword = @"tested keyword #$~$##@#"; 56 | XCTAssertEqualObjects(request.keyword, @"tested keyword #$~$##@#", @"keyword is wrong"); 57 | 58 | // Language 59 | XCTAssertNotNil(request.language, @"default language should not be nil"); 60 | request.language = @"cs"; 61 | XCTAssertEqualObjects(request.language, @"cs", @"language is wrong"); 62 | 63 | // Names 64 | XCTAssertNil(request.names, @"default names should be nil"); 65 | request.names = @[@"testname", @"agsasg*{;'[>–"]; 66 | NSArray *expectedNames = @[@"testname", @"agsasg*{;'[>–"]; 67 | XCTAssertEqualObjects(request.names, expectedNames, @"names is wrong"); 68 | 69 | // min and max price 70 | XCTAssertEqual(request.minPrice, (NSUInteger)NSUIntegerMax, @"default minPrice is wrong"); 71 | XCTAssertEqual(request.maxPrice, (NSUInteger)NSUIntegerMax, @"default maxPrice is wrong"); 72 | 73 | // Test accessors are properly adjusting values 74 | request.minPrice = 123; 75 | XCTAssertEqual(request.minPrice, (NSUInteger)4, @"minPrice bigger value should be 4"); 76 | 77 | request.maxPrice = 3243; 78 | XCTAssertEqual(request.maxPrice, (NSUInteger)4, @"maxPrice bigger value should be 4"); 79 | 80 | 81 | // Open now 82 | XCTAssertFalse(request.openNow, @"default openNow is wrong"); 83 | request.openNow = YES; 84 | XCTAssertTrue(request.openNow, @"openNow is wrong"); 85 | 86 | // Rank by 87 | XCTAssertEqual(request.rankBy, FTGooglePlacesAPIRequestParamRankByProminence, @"default rankBy is wrong"); 88 | request.rankBy = FTGooglePlacesAPIRequestParamRankByDistance; 89 | XCTAssertEqual(request.rankBy, FTGooglePlacesAPIRequestParamRankByDistance, @"rankyBy is wrong"); 90 | 91 | // Types 92 | XCTAssertNil(request.types, @"default types should be nil"); 93 | request.types = @[@"mytype", @"testtype"]; 94 | NSArray *expectedTypes = @[@"mytype", @"testtype"]; 95 | XCTAssertEqualObjects(request.types, expectedTypes, @"types are wrong"); 96 | 97 | // Page token 98 | XCTAssertNil(request.pageToken, @"default page token is wrong"); 99 | request.pageToken = @"dnagadguiabdsguab"; 100 | XCTAssertEqualObjects(request.pageToken, @"dnagadguiabdsguab", @"pageToken is wrong"); 101 | } 102 | 103 | - (void)testProtocolRequestType 104 | { 105 | FTGooglePlacesAPINearbySearchRequest *request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:_locationCoordinate]; 106 | XCTAssertEqualObjects([request placesAPIRequestMethod], @"nearbysearch", @"request type is wrong"); 107 | } 108 | 109 | - (void)testProtocolParameters1 110 | { 111 | FTGooglePlacesAPINearbySearchRequest *request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:_locationCoordinate]; 112 | XCTAssertEqualObjects([request placesAPIRequestMethod], @"nearbysearch", @"request type is wrong"); 113 | 114 | request.keyword = @"test keyword"; 115 | request.rankBy = FTGooglePlacesAPIRequestParamRankByDistance; 116 | request.types = @[@"art_gallery", @"museum"]; 117 | request.language = @"cs"; 118 | request.openNow = YES; 119 | 120 | NSDictionary *expectedOutput = @{ 121 | @"location": @"51.5011030,-0.1245650", 122 | @"keyword": @"test+keyword", 123 | @"rankby": @"distance", 124 | @"types": @"art_gallery|museum", 125 | @"language": @"cs", 126 | @"opennow": [NSNull null], 127 | }; 128 | XCTAssertEqualObjects([request placesAPIRequestParams], expectedOutput, @"ouput params are wrong"); 129 | } 130 | 131 | - (void)testProtocolParameters2 132 | { 133 | FTGooglePlacesAPINearbySearchRequest *request = [[FTGooglePlacesAPINearbySearchRequest alloc] initWithLocationCoordinate:_locationCoordinate]; 134 | XCTAssertEqualObjects([request placesAPIRequestMethod], @"nearbysearch", @"request type is wrong"); 135 | 136 | request.rankBy = FTGooglePlacesAPIRequestParamRankByProminence; 137 | request.types = @[@"store"]; 138 | 139 | NSDictionary *expectedOutput = @{ 140 | @"location": @"51.5011030,-0.1245650", 141 | @"types": @"store", 142 | @"rankby": @"prominence", 143 | @"language": @"en", 144 | @"radius": @"5000" 145 | }; 146 | XCTAssertEqualObjects([request placesAPIRequestParams], expectedOutput, @"ouput params are wrong"); 147 | } 148 | 149 | @end 150 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/FTGooglePlacesAPIResponseTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIResponseTests.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 05/12/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPITestCase.h" 10 | 11 | #import "FTGooglePlacesAPIResponse.h" 12 | 13 | @interface FTGooglePlacesAPIResponseTests : FTGooglePlacesAPITestCase 14 | 15 | @end 16 | 17 | @implementation FTGooglePlacesAPIResponseTests 18 | 19 | - (void)testSimpleInit 20 | { 21 | FTGooglePlacesAPIResponse *response; 22 | 23 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:nil request:nil]; 24 | XCTAssertNil(response, @""); 25 | 26 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:@{} request:nil]; 27 | XCTAssertNil(response, @""); 28 | 29 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:@{ @"key" : @"value" } request:nil]; 30 | XCTAssertNotNil(response, @""); 31 | } 32 | 33 | - (void)testSharedPropertiesAreSetAfterInit 34 | { 35 | // It is expected the response to check number of objects in a dictionary first 36 | NSDictionary *testDictionary = @{ @"key" : @"value" }; 37 | id dictionaryMock = [OCMockObject partialMockForObject:testDictionary]; 38 | [[[dictionaryMock expect] andForwardToRealObject] count]; 39 | 40 | id requestMock = [OCMockObject mockForProtocol:@protocol(FTGooglePlacesAPIRequest)]; 41 | 42 | FTGooglePlacesAPIResponse *response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:dictionaryMock request:requestMock]; 43 | 44 | XCTAssertEqual(response.originalResponseDictionary, dictionaryMock, @""); 45 | [dictionaryMock verify]; 46 | 47 | XCTAssertEqual(response.request, requestMock, @""); 48 | } 49 | 50 | - (void)testStatusIsProperlyParsed 51 | { 52 | FTGooglePlacesAPIResponse *response; 53 | 54 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:@{@"status": @"jasngbgbubguobgO"} request:nil]; 55 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusUnknown, @"status should be FTGooglePlacesAPIResponseStatusUnknown"); 56 | 57 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:@{@"status": @"OK"} request:nil]; 58 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusOK, @"status should be FTGooglePlacesAPIResponseStatusOK"); 59 | 60 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:@{@"status": @"ZERO_RESULTS"} request:nil]; 61 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusNoResults, @"status should be FTGooglePlacesAPIResponseStatusNoResults"); 62 | 63 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:@{@"status": @"OVER_QUERY_LIMIT"} request:nil]; 64 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusAPILimitExceeded, @"status should be FTGooglePlacesAPIResponseStatusAPILimitExceeded"); 65 | 66 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:@{@"status": @"REQUEST_DENIED"} request:nil]; 67 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusRequestDenied, @"status should be FTGooglePlacesAPIResponseStatusRequestDenied"); 68 | 69 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:@{@"status": @"INVALID_REQUEST"} request:nil]; 70 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusInvalidRequest, @"status should be FTGooglePlacesAPIResponseStatusInvalidRequest"); 71 | 72 | response = [[FTGooglePlacesAPIResponse alloc] initWithDictionary:@{@"status": @"NOT_FOUND"} request:nil]; 73 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusNotFound, @"status should be FTGooglePlacesAPIResponseStatusNotFound"); 74 | } 75 | 76 | - (void)testAllStatusesHaveLocalizedName 77 | { 78 | NSString *name; 79 | 80 | name = [FTGooglePlacesAPIResponse localizedNameOfStatus:FTGooglePlacesAPIResponseStatusUnknown]; 81 | XCTAssert([name length] > 0, @"FTGooglePlacesAPIResponseStatusUnknown status does not have localized name"); 82 | 83 | name = [FTGooglePlacesAPIResponse localizedNameOfStatus:FTGooglePlacesAPIResponseStatusOK]; 84 | XCTAssert([name length] > 0, @"FTGooglePlacesAPIResponseStatusOK status does not have localized name"); 85 | 86 | name = [FTGooglePlacesAPIResponse localizedNameOfStatus:FTGooglePlacesAPIResponseStatusNoResults]; 87 | XCTAssert([name length] > 0, @"FTGooglePlacesAPIResponseStatusNoResults status does not have localized name"); 88 | 89 | name = [FTGooglePlacesAPIResponse localizedNameOfStatus:FTGooglePlacesAPIResponseStatusAPILimitExceeded]; 90 | XCTAssert([name length] > 0, @"FTGooglePlacesAPIResponseStatusAPILimitExceeded status does not have localized name"); 91 | 92 | name = [FTGooglePlacesAPIResponse localizedNameOfStatus:FTGooglePlacesAPIResponseStatusRequestDenied]; 93 | XCTAssert([name length] > 0, @"FTGooglePlacesAPIResponseStatusRequestDenied status does not have localized name"); 94 | 95 | name = [FTGooglePlacesAPIResponse localizedNameOfStatus:FTGooglePlacesAPIResponseStatusInvalidRequest]; 96 | XCTAssert([name length] > 0, @"FTGooglePlacesAPIResponseStatusInvalidRequest status does not have localized name"); 97 | 98 | name = [FTGooglePlacesAPIResponse localizedNameOfStatus:FTGooglePlacesAPIResponseStatusNotFound]; 99 | XCTAssert([name length] > 0, @"FTGooglePlacesAPIResponseStatusNotFound status does not have localized name"); 100 | } 101 | 102 | - (void)testAllStatusesHaveLocalizedDescription 103 | { 104 | NSString *description; 105 | 106 | description = [FTGooglePlacesAPIResponse localizedDescriptionForStatus:FTGooglePlacesAPIResponseStatusUnknown]; 107 | XCTAssert([description length] > 0, @"FTGooglePlacesAPIResponseStatusUnknown status does not have a localized description"); 108 | 109 | description = [FTGooglePlacesAPIResponse localizedDescriptionForStatus:FTGooglePlacesAPIResponseStatusOK]; 110 | XCTAssert([description length] > 0, @"FTGooglePlacesAPIResponseStatusOK status does not have a localized description"); 111 | 112 | description = [FTGooglePlacesAPIResponse localizedDescriptionForStatus:FTGooglePlacesAPIResponseStatusNoResults]; 113 | XCTAssert([description length] > 0, @"FTGooglePlacesAPIResponseStatusNoResults status does not have a localized description"); 114 | 115 | description = [FTGooglePlacesAPIResponse localizedDescriptionForStatus:FTGooglePlacesAPIResponseStatusAPILimitExceeded]; 116 | XCTAssert([description length] > 0, @"FTGooglePlacesAPIResponseStatusAPILimitExceeded status does not have a localized description"); 117 | 118 | description = [FTGooglePlacesAPIResponse localizedDescriptionForStatus:FTGooglePlacesAPIResponseStatusRequestDenied]; 119 | XCTAssert([description length] > 0, @"FTGooglePlacesAPIResponseStatusRequestDenied status does not have a localized description"); 120 | 121 | description = [FTGooglePlacesAPIResponse localizedDescriptionForStatus:FTGooglePlacesAPIResponseStatusInvalidRequest]; 122 | XCTAssert([description length] > 0, @"FTGooglePlacesAPIResponseStatusInvalidRequest status does not have a localized description"); 123 | 124 | description = [FTGooglePlacesAPIResponse localizedDescriptionForStatus:FTGooglePlacesAPIResponseStatusNotFound]; 125 | XCTAssert([description length] > 0, @"FTGooglePlacesAPIResponseStatusNotFound status does not have a localized description"); 126 | } 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/FTGooglePlacesAPISearchResponseTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIResponseTests.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 20/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPITestCase.h" 10 | 11 | #import "FTGooglePlacesAPISearchResponse.h" 12 | #import "FTGooglePlacesAPISearchResultItem.h" 13 | #import "FTGooglePlacesAPIDictionaryRequest.h" 14 | 15 | #import "MockFTGooglePlacesAPISearchResultItemSubclass.h" 16 | 17 | @interface FTGooglePlacesAPISearchResponseTests : FTGooglePlacesAPITestCase 18 | 19 | @end 20 | 21 | @implementation FTGooglePlacesAPISearchResponseTests 22 | 23 | - (void)setUp 24 | { 25 | [super setUp]; 26 | // Put setup code here; it will be run once, before the first test case. 27 | } 28 | 29 | - (void)tearDown 30 | { 31 | // Put teardown code here; it will be run once, after the last test case. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testBasicInitializers 36 | { 37 | FTGooglePlacesAPISearchResponse *response; 38 | 39 | response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:nil request:nil]; 40 | XCTAssertNil(response, @"response should be nil"); 41 | XCTAssertNil(response.request, @"request should be nil"); 42 | 43 | response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:nil request:nil resultsItemClass:nil]; 44 | XCTAssertNil(response, @"response should be nil"); 45 | XCTAssertNil(response.request, @"request should be nil"); 46 | 47 | response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:@{} request:nil]; 48 | XCTAssertNil(response, @"response should be nil"); 49 | XCTAssertNil(response.request, @"request should be nil"); 50 | 51 | response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:@{} request:nil]; 52 | XCTAssertNil(response, @"response should be nil"); 53 | XCTAssertNil(response.request, @"request should be nil"); 54 | 55 | response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:@{@"status": @"OK"} request:nil]; 56 | XCTAssertNotNil(response, @"response should be non-nil"); 57 | XCTAssertNil(response.request, @"request should be nil"); 58 | } 59 | 60 | - (void)testBasicProperties 61 | { 62 | NSDictionary *dictionary = @{ 63 | @"html_attributions": @[@"atribution1", @"attribution2"], 64 | @"results": [NSNull null], 65 | @"status": @"OK" 66 | }; 67 | 68 | FTGooglePlacesAPISearchResponse *response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:dictionary request:nil]; 69 | 70 | XCTAssertNotNil(response.htmlAttributions, @"htmlAttributions should not be nil"); 71 | XCTAssertEqualObjects(@([response.htmlAttributions count]), @2, @"wrong number of htmlAttributions"); 72 | NSArray *expectedHtmlAttributions = @[@"atribution1", @"attribution2"]; 73 | XCTAssertEqualObjects(response.htmlAttributions, expectedHtmlAttributions, @"wrong htmlAttributions"); 74 | XCTAssertNil(response.request, @"request should be nil"); 75 | 76 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusOK, @"status is wrong"); 77 | 78 | XCTAssertNil(response.results, @"results should be nil"); 79 | } 80 | 81 | - (void)testEmptyInputs 82 | { 83 | FTGooglePlacesAPISearchResponse *response; 84 | 85 | response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:nil request:nil]; 86 | XCTAssertNil(response, @"response should be nil"); 87 | 88 | response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:@{} request:nil]; 89 | XCTAssertNil(response, @"response should be nil"); 90 | } 91 | 92 | - (void)testProperResponseClassIsChecked 93 | { 94 | // Check that it works with the proper subclass 95 | id mockSubclass = [[MockFTGooglePlacesAPISearchResultItemSubclass alloc] initWithDictionary:@{@"key":@"value"}]; 96 | 97 | XCTAssertNoThrow([[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:@{} request:nil resultsItemClass:mockSubclass], @"Should not throw exception as the provided class is right"); 98 | 99 | 100 | // Check expecption is throws if resultItemClass is specified, but is not the proper subclass 101 | XCTAssertThrows([[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:@{} request:nil resultsItemClass:[NSObject class]], @"Should throw exception when the resultsItemClass is not subclass of FTGooglePlacesAPISearchResultItem"); 102 | } 103 | 104 | - (void)testRequestAndNextPageRequestMethod 105 | { 106 | NSDictionary *dictionary = [[self class] JSONFromFileNamed:@"FTGooglePlacesAPIResponse-test1-Nearby-Search-OK.json"]; 107 | 108 | FTGooglePlacesAPIDictionaryRequest *request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:@{@"testkey": @"testvalue"} requestType:@"testrequest"]; 109 | 110 | FTGooglePlacesAPISearchResponse *response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:dictionary request:request]; 111 | 112 | XCTAssertNotNil(request, @"request should not be nil"); 113 | XCTAssertNotNil(response, @"response should not be nil"); 114 | 115 | XCTAssertEqual(response.request, request, @"request is wrong"); 116 | 117 | idnextPageRequest = [response nextPageRequest]; 118 | XCTAssertNotNil(nextPageRequest, @"nextPageRequest should not be nil"); 119 | 120 | XCTAssertEqualObjects([nextPageRequest placesAPIRequestMethod], @"testrequest", @"nextPageRequest requestTypeUrlString is wrong"); 121 | NSDictionary *expectedRequestParams = @{@"pagetoken": @"CoQCAAEAACUEBVd58EGiJKFOe1KoEFks9jia46vhv6Bi4sR0ExZaFgvu6XqK2xgB6RXYqdWX1QP26UlcCR7DGiBwhH2-FN528X9J-CUH0JjUxauAzJjuxhyyxt946AW1ETRqecANfJdZ5_cjRwhR-0qQ13HeiQaB_riQPMG4SAqKcb8OMcHMtSJrJUacIIifXTNyxJpEbP8dvETT-pDM-0zl9qmTO0aqGdmz0EzyPAEuMCmZEMUI0gnZJvvDvaar_L4KCL6bhjGFqs5OrW-DIn1Vo5CI8bVD4VRINLdxAk7ovLOxT04p1M9qo6JNJ5ZZYntyX2MC64SvIe2RtFIGDGy046xJho4SECVEEiNhLUaQw1_2dUSO4zUaFKqr08lGcqbMqGhRJ9VG69t6YMgj"}; 122 | XCTAssertEqualObjects([nextPageRequest placesAPIRequestParams], expectedRequestParams, @"pageToken returned by nextPageRequest: is wrong"); 123 | } 124 | 125 | - (void)testParsingRealResponseOK 126 | { 127 | NSDictionary *dictionary = [[self class] JSONFromFileNamed:@"FTGooglePlacesAPIResponse-test1-Nearby-Search-OK.json"]; 128 | 129 | FTGooglePlacesAPIDictionaryRequest *request = [[FTGooglePlacesAPIDictionaryRequest alloc] initWithDictionary:@{@"testkey": @"testvalue"} requestType:@"testrequest"]; 130 | 131 | FTGooglePlacesAPISearchResponse *response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:dictionary request:request]; 132 | 133 | XCTAssertNotNil(response, @"response should not be nil"); 134 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusOK, @"status should be FTGooglePlacesAPIResponseStatusOK"); 135 | XCTAssertEqualObjects(response.nextPageToken, @"CoQCAAEAACUEBVd58EGiJKFOe1KoEFks9jia46vhv6Bi4sR0ExZaFgvu6XqK2xgB6RXYqdWX1QP26UlcCR7DGiBwhH2-FN528X9J-CUH0JjUxauAzJjuxhyyxt946AW1ETRqecANfJdZ5_cjRwhR-0qQ13HeiQaB_riQPMG4SAqKcb8OMcHMtSJrJUacIIifXTNyxJpEbP8dvETT-pDM-0zl9qmTO0aqGdmz0EzyPAEuMCmZEMUI0gnZJvvDvaar_L4KCL6bhjGFqs5OrW-DIn1Vo5CI8bVD4VRINLdxAk7ovLOxT04p1M9qo6JNJ5ZZYntyX2MC64SvIe2RtFIGDGy046xJho4SECVEEiNhLUaQw1_2dUSO4zUaFKqr08lGcqbMqGhRJ9VG69t6YMgj", @"nextPageToken is wrong"); 136 | XCTAssertNotNil([response nextPageRequest], @"nextPageRequest should not be nil"); 137 | 138 | XCTAssertEqual([response.results count], (NSUInteger)20, @"response should have 20 results items"); 139 | XCTAssertEqual([response.results[0] class], [FTGooglePlacesAPISearchResultItem class], @"repsonse item should be of class FTGooglePlacesAPISearchResultItem"); 140 | } 141 | 142 | - (void)testParsingRealResponseNoResults 143 | { 144 | NSDictionary *dictionary = [[self class] JSONFromFileNamed:@"FTGooglePlacesAPIResponse-test2-NoResults.json"]; 145 | 146 | FTGooglePlacesAPISearchResponse *response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:dictionary request:nil]; 147 | 148 | XCTAssertNotNil(response, @"response should not be nil"); 149 | XCTAssertNil(response.results, @"results should be nil"); 150 | XCTAssertNil(response.nextPageToken, @"nextPageToken should be nil"); 151 | XCTAssertNil([response nextPageRequest], @"nextPageRequest should be nil"); 152 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusNoResults, @"status should be FTGooglePlacesAPIResponseStatusNoResults"); 153 | } 154 | 155 | - (void)testParsingRealResponseInvalidRequest 156 | { 157 | NSDictionary *dictionary = [[self class] JSONFromFileNamed:@"FTGooglePlacesAPIResponse-test3-InvalidRequest.json"]; 158 | 159 | FTGooglePlacesAPISearchResponse *response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:dictionary request:nil]; 160 | 161 | XCTAssertNotNil(response, @"response should not be nil"); 162 | XCTAssertNil(response.results, @"results should be nil"); 163 | XCTAssertNil(response.nextPageToken, @"nextPageToken should be nil"); 164 | XCTAssertNil([response nextPageRequest], @"nextPageRequest should be nil"); 165 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusInvalidRequest, @"status should be FTGooglePlacesAPIResponseStatusInvalidRequest"); 166 | } 167 | 168 | - (void)testParsingRealResponseRequestDenied 169 | { 170 | NSDictionary *dictionary = [[self class] JSONFromFileNamed:@"FTGooglePlacesAPIResponse-test4-RequestDenied.json"]; 171 | 172 | FTGooglePlacesAPISearchResponse *response = [[FTGooglePlacesAPISearchResponse alloc] initWithDictionary:dictionary request:nil]; 173 | 174 | XCTAssertNotNil(response, @"response should not be nil"); 175 | XCTAssertNil(response.results, @"results should be nil"); 176 | XCTAssertNil(response.nextPageToken, @"nextPageToken should be nil"); 177 | XCTAssertNil([response nextPageRequest], @"nextPageRequest should be nil"); 178 | XCTAssertEqual(response.status, FTGooglePlacesAPIResponseStatusRequestDenied, @"status should be FTGooglePlacesAPIResponseStatusRequestDenied"); 179 | } 180 | 181 | @end 182 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/FTGooglePlacesAPISearchResultItemTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIResponseItemTests.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 20/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "FTGooglePlacesAPISearchResultItem.h" 12 | 13 | 14 | @interface FTGooglePlacesAPISearchResultItemTests : XCTestCase 15 | 16 | @end 17 | 18 | 19 | @implementation FTGooglePlacesAPISearchResultItemTests 20 | 21 | - (void)testParsingFromManualDictionary 22 | { 23 | // Example output JSON examples 24 | NSDictionary *dictionary = @{ 25 | @"geometry": @{ 26 | @"location": @{ 27 | @"lat": @(51.502514), 28 | @"lng": @(-0.118782) 29 | } 30 | }, 31 | @"icon": @"http://maps.gstatic.com/mapfiles/place_api/icons/museum-71.png", 32 | @"id": @"5139d09fbccf4c974fce66f7d88269c149bac0a5", 33 | @"name": @"London Film Museum South Bank", 34 | @"opening_hours": @{ 35 | @"open_now": @(YES) 36 | }, 37 | @"photos": @[ 38 | @{ 39 | @"height": @(1154), 40 | @"html_attributions": @[@"cesar vazquez"], 41 | @"photo_reference": @"CpQBhwAAAPP1yLpftkF_7O6WJpEM-SNyONWXeonrVGI5IJv77BOW3kzRmnpVS6e9Ig1eExkAvi0", 42 | @"width": @(2048) 43 | } 44 | ], 45 | @"rating": @(3.6), 46 | @"place_id": @"ChIJyWEHuEmuEmsRm9hTkapTCrk", 47 | @"reference": @"CoQBgAAAAFjx_1xLA2D3kuZ83rCChllMEaGrxSeoPh6", 48 | @"types": @[@"museum", @"establishment"], 49 | @"vicinity": @"1st Floor, Riverside Building, County Hall, London" 50 | }; 51 | 52 | 53 | FTGooglePlacesAPISearchResultItem *item = [[FTGooglePlacesAPISearchResultItem alloc] initWithDictionary:dictionary]; 54 | XCTAssertNotNil(item, @"Initialiazation should succeed"); 55 | 56 | XCTAssertEqualObjects(item.placeId, @"ChIJyWEHuEmuEmsRm9hTkapTCrk", @"placeId is wrong"); 57 | XCTAssertEqualObjects(item.name, @"London Film Museum South Bank", @"name is wrong"); 58 | 59 | XCTAssertNotNil(item.location, @"location should be non-nil"); 60 | XCTAssertEqual(item.location.coordinate.latitude, 51.502514, @"location.latitude is wrong"); 61 | XCTAssertEqual(item.location.coordinate.longitude, -0.118782, @"location.longitude is wrong"); 62 | 63 | XCTAssertEqualObjects(item.addressString, @"1st Floor, Riverside Building, County Hall, London"); 64 | 65 | XCTAssertEqual(item.openedState, FTGooglePlacesAPISearchResultItemOpenedStateOpened, @"openedState is wrong"); 66 | XCTAssertEqualObjects(item.iconImageUrl, @"http://maps.gstatic.com/mapfiles/place_api/icons/museum-71.png", @"iconImageUrl is wrong"); 67 | XCTAssertEqual(item.rating, 3.6f, @"rating is wrong"); 68 | XCTAssertEqualObjects(item.reference, @"CoQBgAAAAFjx_1xLA2D3kuZ83rCChllMEaGrxSeoPh6", @"reference is wrong"); 69 | 70 | NSArray *typesExpectedResult = @[@"museum", @"establishment"]; 71 | XCTAssertEqualObjects(item.types, typesExpectedResult, @"types are wrong"); 72 | 73 | XCTAssertEqualObjects(item.originalDictionaryRepresentation, [dictionary copy], @"originalDictionaryRepresentation is wrong"); 74 | } 75 | 76 | - (void)testVeryBasicObjectWithJustId 77 | { 78 | FTGooglePlacesAPISearchResultItem *item = [[FTGooglePlacesAPISearchResultItem alloc] initWithDictionary:@{@"place_id": @"someValue"}]; 79 | XCTAssertNotNil(item, @"item should be valid once it has placeId"); 80 | 81 | item = [[FTGooglePlacesAPISearchResultItem alloc] initWithDictionary:@{}]; 82 | XCTAssertNil(item, @"item should be nil when no id is provided"); 83 | 84 | item = [[FTGooglePlacesAPISearchResultItem alloc] initWithDictionary:@{@"id": @""}]; 85 | XCTAssertNil(item, @"item should be nil when id has 0 length"); 86 | } 87 | 88 | - (void)testEqualityChecking 89 | { 90 | NSDictionary *dictionary1a = @{@"place_id": @"5139d09fbccf4c974fce66f7d88269c149bac0a5"}; 91 | NSDictionary *dictionary1b = @{@"place_id": @"5139d09fbccf4c974fce66f7d88269c149bac0a5"}; 92 | 93 | FTGooglePlacesAPISearchResultItem *item1a = [[FTGooglePlacesAPISearchResultItem alloc] initWithDictionary:dictionary1a]; 94 | FTGooglePlacesAPISearchResultItem *item1b = [[FTGooglePlacesAPISearchResultItem alloc] initWithDictionary:dictionary1b]; 95 | 96 | XCTAssertEqualObjects(item1a, item1b, @"object are not considered to be equal, but should be"); 97 | 98 | 99 | NSDictionary *dictionary2a = @{@"place_id": @"5139d09fbccf4c974fce66f7d88269c149bac0a5"}; 100 | NSDictionary *dictionary2b = @{@"place_id": @"XAXAGFKANGOKANOAOGNAOGOIANGOI AOAANGO"}; 101 | 102 | FTGooglePlacesAPISearchResultItem *item2a = [[FTGooglePlacesAPISearchResultItem alloc] initWithDictionary:dictionary2a]; 103 | FTGooglePlacesAPISearchResultItem *item2b = [[FTGooglePlacesAPISearchResultItem alloc] initWithDictionary:dictionary2b]; 104 | 105 | XCTAssertNotEqualObjects(item2a, item2b, @"object with differents ids should not be considered to be equal"); 106 | } 107 | 108 | - (void)testEmptyValues 109 | { 110 | NSDictionary *dictionary = @{@"place_id": @"5139d09fbccf4c974fce66f7d88269c149bac0a5"}; 111 | 112 | FTGooglePlacesAPISearchResultItem *item = [[FTGooglePlacesAPISearchResultItem alloc] initWithDictionary:dictionary]; 113 | 114 | XCTAssertNil(item.name, @"name should be nil"); 115 | XCTAssertNil(item.location, @"location should be nil"); 116 | XCTAssertNil(item.addressString, @"addressString should be nil"); 117 | XCTAssertEqual(item.openedState, FTGooglePlacesAPISearchResultItemOpenedStateUnknown, @"openedState should be Unknown"); 118 | XCTAssertNil(item.iconImageUrl, @"iconImageUrl should be nil"); 119 | XCTAssertEqual(item.rating, 0.0f, @"rating should be 0.0f"); 120 | XCTAssertNil(item.reference, @"reference should be nil"); 121 | XCTAssertNil(item.types, @"types should be nil"); 122 | 123 | XCTAssertEqualObjects(item.originalDictionaryRepresentation, @{@"place_id": @"5139d09fbccf4c974fce66f7d88269c149bac0a5"}, @"originalDictionaryRepresentation is wrong"); 124 | } 125 | 126 | @end 127 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/FTGooglePlacesAPIServiceTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPIServiceTests.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 05/12/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPITestCase.h" 10 | 11 | #import "FTGooglePlacesAPI.h" 12 | 13 | #import "AFHTTPRequestOperationManager.h" 14 | 15 | #import "MockFTGooglePlacesAPIService.h" 16 | #import "MockAFHTTPRequestOperationManager.h" 17 | #import "MockFTGooglePlacesAPISearchResultItemSubclass.h" 18 | 19 | 20 | /** 21 | * This category exposes internal private interface and methods of a FTGooglePlacesAPIService. 22 | * This is a helper for testing, because the interface is minimalistic, based on class methods, 23 | * and as such would be very difficult to test. 24 | */ 25 | @interface FTGooglePlacesAPIService (PrivateTestsHelpers) 26 | 27 | @property (nonatomic, strong) AFHTTPRequestOperationManager *httpRequestOperationManager; 28 | @property (nonatomic, copy) NSString *apiKey; 29 | @property (nonatomic, weak) Class searchResultsItemClass; 30 | 31 | + (instancetype)sharedService; 32 | 33 | + (void)executeRequest:(id)request 34 | withCompletionHandler:(void(^)(NSDictionary *responseObject, NSError *error))completion; 35 | 36 | @end 37 | 38 | 39 | @interface FTGooglePlacesAPIServiceTests : FTGooglePlacesAPITestCase 40 | 41 | @end 42 | 43 | @implementation FTGooglePlacesAPIServiceTests 44 | 45 | - (void)setUp 46 | { 47 | [super setUp]; 48 | // Put setup code here; it will be run once, before the first test case. 49 | } 50 | 51 | - (void)tearDown 52 | { 53 | // Put teardown code here; it will be run once, after the last test case. 54 | [super tearDown]; 55 | } 56 | 57 | #pragma mark - Configuration tests 58 | 59 | - (void)testAPIKeyIsForwardedToSingletonInstance 60 | { 61 | NSString *apiKey = @"testingAPIKey"; 62 | 63 | // Create mock for service singleton instance and expect setApiKey to be called 64 | id mockService = [OCMockObject mockForClass:[FTGooglePlacesAPIService class]]; 65 | [[mockService expect] setApiKey:apiKey]; 66 | 67 | // Use mock service object with a custom singleton instance 68 | [MockFTGooglePlacesAPIService setSingletonInstance:mockService]; 69 | 70 | // And call a regular public method 71 | [MockFTGooglePlacesAPIService provideAPIKey:apiKey]; 72 | 73 | [mockService verify]; 74 | } 75 | 76 | - (void)testCustomSearchResultsItemClassIsForwardedToSingletonInstance 77 | { 78 | id mockCustomClass = [OCMockObject mockForClass:[NSObject class]]; 79 | 80 | // Create mock for service singleton instance and setSearchResultsItemClass to be called 81 | id mockService = [OCMockObject mockForClass:[FTGooglePlacesAPIService class]]; 82 | [[mockService expect] setSearchResultsItemClass:mockCustomClass]; 83 | 84 | // Use mock service object with a custom singleton instance 85 | [MockFTGooglePlacesAPIService setSingletonInstance:mockService]; 86 | 87 | // And call a regular public method 88 | [MockFTGooglePlacesAPIService registerSearchResultItemClass:mockCustomClass]; 89 | 90 | [mockService verify]; 91 | } 92 | 93 | #pragma mark - Requests tests 94 | 95 | - (void)testServiceUsesAllRequiredMethodsFromRequestProtocol 96 | { 97 | id mockRequest = [OCMockObject mockForProtocol:@protocol(FTGooglePlacesAPIRequest)]; 98 | id mockHTTPManager = [OCMockObject niceMockForClass:[AFHTTPRequestOperationManager class]]; 99 | 100 | [[[mockRequest expect] andReturn:@"testrequesttype"] placesAPIRequestMethod]; 101 | [[[mockRequest expect] andReturn:@{ @"key": @"value" }] placesAPIRequestParams]; 102 | 103 | // Mock service 104 | [self configureBasicMockService]; 105 | [MockFTGooglePlacesAPIService sharedService].httpRequestOperationManager = mockHTTPManager; 106 | 107 | [MockFTGooglePlacesAPIService executeRequest:mockRequest withCompletionHandler:^(NSDictionary *responseObject, NSError *error) {}]; 108 | 109 | [mockRequest verify]; 110 | } 111 | 112 | - (void)testServiceContructsProperRequestURL 113 | { 114 | [MockFTGooglePlacesAPIService createDefaultSingletonInstance]; 115 | [MockFTGooglePlacesAPIService provideAPIKey:@"fakeAPIKey"]; 116 | 117 | MockAFHTTPRequestOperationManager *mockHTTPManager = [[MockAFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:FTGooglePlacesAPIBaseURL]]; 118 | [MockFTGooglePlacesAPIService sharedService].httpRequestOperationManager = mockHTTPManager; 119 | 120 | id mockRequest = [OCMockObject mockForProtocol:@protocol(FTGooglePlacesAPIRequest)]; 121 | [[[mockRequest expect] andReturn:@"testtype"] placesAPIRequestMethod]; 122 | [[[mockRequest expect] andReturn:@{ 123 | @"xkey1": @"value1", 124 | @"xkey2": @"value2" 125 | }] placesAPIRequestParams]; 126 | 127 | 128 | NSString *expectedURLStringPrefix = @"https://maps.googleapis.com/maps/api/place/testtype/json?"; 129 | 130 | [MockFTGooglePlacesAPIService executeRequest:mockRequest withCompletionHandler:^(NSDictionary *responseObject, NSError *error) {}]; 131 | 132 | // We can only test for a prefix because the params are dictionary and we cannot 133 | // rely on exact order of parameters 134 | XCTAssertEqualObjects(mockHTTPManager.lastURLString, @"testtype/json", @""); 135 | XCTAssert([[[mockHTTPManager.lastURLRequest URL] absoluteString] hasPrefix:expectedURLStringPrefix], @""); 136 | 137 | [mockRequest verify]; 138 | } 139 | 140 | - (void)testExecuteRequestPropagatesErrorToHandlerBlock 141 | { 142 | // Expects 143 | NSError *expectedError = [NSError errorWithDomain:@"TestErrorDomain" code:12345 userInfo:nil]; 144 | 145 | // Configure Service to use fake Mock AFNetworking 146 | [self configureBasicMockService]; 147 | 148 | MockAFHTTPRequestOperationManager *mockHTTPManager = [[MockAFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:FTGooglePlacesAPIBaseURL]]; 149 | [MockFTGooglePlacesAPIService sharedService].httpRequestOperationManager = mockHTTPManager; 150 | 151 | 152 | [mockHTTPManager setFailureErrorToReturn:expectedError]; 153 | 154 | // Test general public interface 155 | id mockRequest = [self createMockRequest]; 156 | 157 | [MockFTGooglePlacesAPIService executeSearchRequest:mockRequest withCompletionHandler:^(FTGooglePlacesAPISearchResponse *response, NSError *error) { 158 | 159 | XCTAssertNil(response, @"Response should be nil when the error is not caused by API response code"); 160 | XCTAssertEqualObjects(error, expectedError, @"Error should be passed to Search request handler block"); 161 | }]; 162 | [mockRequest verify]; 163 | 164 | mockRequest = [self createMockRequest]; 165 | [MockFTGooglePlacesAPIService executeDetailRequest:mockRequest withCompletionHandler:^(FTGooglePlacesAPIDetailResponse *response, NSError *error) { 166 | 167 | XCTAssertNil(response, @"Response should be nil when the error is not caused by API response code"); 168 | XCTAssertEqualObjects(error, expectedError, @"Error should be passed to Detail request handler block"); 169 | }]; 170 | 171 | [mockRequest verify]; 172 | } 173 | 174 | - (void)testServiceParsesSearchResultsAndPassesThemToHandlerBlock 175 | { 176 | MockAFHTTPRequestOperationManager *mockManager; 177 | 178 | // Load valid JSON response 179 | id responseObject = [[self class] JSONFromFileNamed:@"FTGooglePlacesAPIResponse-test1-Nearby-Search-OK.json"]; 180 | [self configureMockServiceWithResponse:responseObject requestManager:&mockManager]; 181 | mockManager.shouldFireSuccessBlock = YES; 182 | 183 | id mockRequest = [self createMockRequest]; 184 | 185 | [MockFTGooglePlacesAPIService executeSearchRequest:mockRequest withCompletionHandler:^(FTGooglePlacesAPISearchResponse *response, NSError *error) { 186 | 187 | XCTAssertNil(error, @""); 188 | XCTAssertNotNil(response, @""); 189 | XCTAssertEqual([[response results] count], (NSUInteger)20, @"Result should have 20 results"); 190 | 191 | [mockRequest verify]; 192 | }]; 193 | } 194 | 195 | - (void)testServicePassesErrorParsedFromResponseObject 196 | { 197 | MockAFHTTPRequestOperationManager *mockManager; 198 | 199 | // Load valid JSON response 200 | id responseObject = [[self class] JSONFromFileNamed:@"FTGooglePlacesAPIResponse-test2-NoResults.json"]; 201 | [self configureMockServiceWithResponse:responseObject requestManager:&mockManager]; 202 | 203 | id mockRequest = [self createMockRequest]; 204 | 205 | [MockFTGooglePlacesAPIService executeSearchRequest:mockRequest withCompletionHandler:^(FTGooglePlacesAPISearchResponse *response, NSError *error) { 206 | 207 | XCTAssertNotNil(response, @"Response should not be nil when caused by API response status code"); 208 | XCTAssertEqual([response.results count], (NSUInteger)0, @"Response should have no results"); 209 | 210 | XCTAssertNotNil(error, @"Error should be passed"); 211 | XCTAssertEqualObjects(error.domain, FTGooglePlacesAPIErrorDomain, @"Error should be from FTGooglePlacesAPIErrorDomain"); 212 | XCTAssertEqual(error.code, (NSInteger)FTGooglePlacesAPIResponseStatusNoResults, @"Error status code should be set"); 213 | 214 | [mockRequest verify]; 215 | }]; 216 | } 217 | 218 | - (void)testServiceUsesCustomClassForSearchResultsItemsWhenRequested 219 | { 220 | MockAFHTTPRequestOperationManager *mockManager; 221 | 222 | // Load valid JSON response 223 | id responseObject = [[self class] JSONFromFileNamed:@"FTGooglePlacesAPIResponse-test1-Nearby-Search-OK.json"]; 224 | [self configureMockServiceWithResponse:responseObject requestManager:&mockManager]; 225 | mockManager.shouldFireSuccessBlock = YES; 226 | 227 | id mockRequest = [self createMockRequest]; 228 | 229 | // Register custom result class 230 | [MockFTGooglePlacesAPIService registerSearchResultItemClass:[MockFTGooglePlacesAPISearchResultItemSubclass class]]; 231 | 232 | [MockFTGooglePlacesAPIService executeSearchRequest:mockRequest withCompletionHandler:^(FTGooglePlacesAPISearchResponse *response, NSError *error) { 233 | 234 | XCTAssert([[response results] count] > 0, @"At least 1 results should be returned for this test"); 235 | 236 | id resultsItemInstance = [[response results] objectAtIndex:0]; 237 | XCTAssertEqualObjects([resultsItemInstance class], [MockFTGooglePlacesAPISearchResultItemSubclass class], @"Result item should be an instance have requested class"); 238 | 239 | [mockRequest verify]; 240 | }]; 241 | } 242 | 243 | #pragma mark - Utilities 244 | 245 | - (void)configureBasicMockService 246 | { 247 | [MockFTGooglePlacesAPIService createDefaultSingletonInstance]; 248 | [MockFTGooglePlacesAPIService provideAPIKey:@"fakeAPIKey"]; 249 | } 250 | 251 | /** 252 | * Creates simple FTGooglePlacesAPIRequest mock expecting all methods to be called 253 | */ 254 | - (id)createMockRequest 255 | { 256 | id mockRequest = [OCMockObject mockForProtocol:@protocol(FTGooglePlacesAPIRequest)]; 257 | 258 | [[[mockRequest expect] andReturn:@"testtype"] placesAPIRequestMethod]; 259 | [[[mockRequest expect] andReturn:@{@"param1": @"value1"}] placesAPIRequestParams]; 260 | 261 | return mockRequest; 262 | } 263 | 264 | /** 265 | * Creates MockService with a MockAFNetworking 266 | * 267 | * @param responseObject Response object to be returned by the service 268 | * @param mockManager Reference to which the manager instance pointer should be set. Can be NULL (not interested in working with MockAFHTTPRequestOperationManager) 269 | */ 270 | - (void)configureMockServiceWithResponse:(id)responseObject requestManager:(MockAFHTTPRequestOperationManager **)mockManager 271 | { 272 | // Configure Service to use fake Mock AFNetworking 273 | [self configureBasicMockService]; 274 | 275 | MockAFHTTPRequestOperationManager *mockHTTPManager = [[MockAFHTTPRequestOperationManager alloc] initWithBaseURL:nil]; 276 | [MockFTGooglePlacesAPIService sharedService].httpRequestOperationManager = mockHTTPManager; 277 | 278 | // Load valid JSON response 279 | [mockHTTPManager setResponseObjectToReturn:responseObject]; 280 | 281 | if (mockManager) { 282 | *mockManager = mockHTTPManager; 283 | } 284 | } 285 | 286 | 287 | @end 288 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/FTGooglePlacesAPITestCase.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPITestCase.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 20/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface FTGooglePlacesAPITestCase : XCTestCase 13 | 14 | /** 15 | * This a set of helper methods for loading resources required 16 | * in tests 17 | * 18 | */ 19 | + (NSData *)dataFromFileNamed:(NSString *)filename; 20 | + (NSString *)stringFromFileNamed:(NSString *)filename usingEncoding:(NSStringEncoding)encoding; 21 | + (id)JSONFromFileNamed:(NSString *)filename; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/FTGooglePlacesAPITestCase.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTGooglePlacesAPITestCase.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 20/11/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPITestCase.h" 10 | 11 | @implementation FTGooglePlacesAPITestCase 12 | 13 | + (NSData *)dataFromFileNamed:(NSString *)filename 14 | { 15 | NSString *name = [filename stringByDeletingPathExtension]; 16 | NSString *extension = [filename pathExtension]; 17 | 18 | NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:name withExtension:extension]; 19 | 20 | NSData *data = [NSData dataWithContentsOfURL:url]; 21 | 22 | return data; 23 | } 24 | 25 | + (NSString *)stringFromFileNamed:(NSString *)filename usingEncoding:(NSStringEncoding)encoding 26 | { 27 | NSData *data = [[self class] dataFromFileNamed:filename]; 28 | 29 | return [[NSString alloc] initWithData:data encoding:encoding]; 30 | } 31 | 32 | + (id)JSONFromFileNamed:(NSString *)filename 33 | { 34 | NSData *data = [[self class] dataFromFileNamed:filename]; 35 | 36 | id json = [NSJSONSerialization JSONObjectWithData:data 37 | options:0 38 | error:NULL]; 39 | 40 | return json; 41 | } 42 | 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/Mocks/MockAFHTTPRequestOperationManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // MockAFHTTPRequestOperationManager.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 09/12/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "AFHTTPRequestOperationManager.h" 10 | 11 | @interface MockAFHTTPRequestOperationManager : AFHTTPRequestOperationManager 12 | 13 | @property (nonatomic, assign) BOOL shouldFireSuccessBlock; 14 | @property (nonatomic, strong) AFHTTPRequestOperation *requestOperation; 15 | @property (nonatomic, strong, readonly) NSString *lastURLString; 16 | @property (nonatomic, strong, readonly) NSMutableURLRequest *lastURLRequest; 17 | 18 | - (void)setFailureErrorToReturn:(NSError *)error; 19 | - (void)setResponseObjectToReturn:(id)responseObject; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/Mocks/MockAFHTTPRequestOperationManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // MockAFHTTPRequestOperationManager.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 09/12/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "MockAFHTTPRequestOperationManager.h" 10 | 11 | @implementation MockAFHTTPRequestOperationManager { 12 | 13 | BOOL _shouldFireFailureBlock; 14 | NSError *_failureErrorToReturn; 15 | id _responseObject; 16 | } 17 | 18 | #pragma mark Mock implementation 19 | 20 | - (void)setFailureErrorToReturn:(NSError *)error 21 | { 22 | _shouldFireFailureBlock = YES; 23 | _failureErrorToReturn = error; 24 | } 25 | 26 | - (void)setResponseObjectToReturn:(id)responseObject 27 | { 28 | _responseObject = responseObject; 29 | } 30 | 31 | #pragma mark - Superclass mock overrides 32 | 33 | - (AFHTTPRequestOperation *)GET:(NSString *)URLString 34 | parameters:(NSDictionary *)parameters 35 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 36 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 37 | { 38 | _lastURLString = URLString; 39 | _lastURLRequest = [super.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; 40 | 41 | if (_shouldFireSuccessBlock && success) { 42 | success(nil, _responseObject); 43 | } 44 | 45 | if (_shouldFireFailureBlock && failure) { 46 | failure(nil, _failureErrorToReturn); 47 | } 48 | 49 | return _requestOperation; 50 | } 51 | 52 | - (AFHTTPRequestOperation *)HEAD:(NSString *)URLString 53 | parameters:(NSDictionary *)parameters 54 | success:(void (^)(AFHTTPRequestOperation *operation))success 55 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 56 | { 57 | _lastURLString = URLString; 58 | _lastURLRequest = [super.requestSerializer requestWithMethod:@"HEAD" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; 59 | 60 | if (_shouldFireSuccessBlock && success) { 61 | success(nil); 62 | } 63 | 64 | if (_shouldFireFailureBlock && failure) { 65 | failure(nil, _failureErrorToReturn); 66 | } 67 | 68 | return nil; 69 | } 70 | 71 | - (AFHTTPRequestOperation *)POST:(NSString *)URLString 72 | parameters:(NSDictionary *)parameters 73 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 74 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 75 | { 76 | _lastURLString = URLString; 77 | _lastURLRequest = [super.requestSerializer requestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; 78 | 79 | if (_shouldFireSuccessBlock && success) { 80 | success(nil, _responseObject); 81 | } 82 | 83 | if (_shouldFireFailureBlock && failure) { 84 | failure(nil, _failureErrorToReturn); 85 | } 86 | 87 | return nil; 88 | } 89 | 90 | - (AFHTTPRequestOperation *)PUT:(NSString *)URLString 91 | parameters:(NSDictionary *)parameters 92 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 93 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 94 | { 95 | _lastURLString = URLString; 96 | _lastURLRequest = [super.requestSerializer requestWithMethod:@"PUT" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; 97 | 98 | 99 | if (_shouldFireSuccessBlock && success) { 100 | success(nil, _responseObject); 101 | } 102 | 103 | if (_shouldFireFailureBlock && failure) { 104 | failure(nil, _failureErrorToReturn); 105 | } 106 | 107 | return nil; 108 | } 109 | 110 | - (AFHTTPRequestOperation *)PATCH:(NSString *)URLString 111 | parameters:(NSDictionary *)parameters 112 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 113 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 114 | { 115 | _lastURLString = URLString; 116 | _lastURLRequest = [super.requestSerializer requestWithMethod:@"PATCH" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; 117 | 118 | if (_shouldFireSuccessBlock && success) { 119 | success(nil, _responseObject); 120 | } 121 | 122 | if (_shouldFireFailureBlock && failure) { 123 | failure(nil, _failureErrorToReturn); 124 | } 125 | 126 | return nil; 127 | } 128 | 129 | - (AFHTTPRequestOperation *)DELETE:(NSString *)URLString 130 | parameters:(NSDictionary *)parameters 131 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 132 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 133 | { 134 | _lastURLString = URLString; 135 | _lastURLRequest = [super.requestSerializer requestWithMethod:@"DELETE" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil]; 136 | 137 | if (_shouldFireSuccessBlock && success) { 138 | success(nil, _responseObject); 139 | } 140 | 141 | if (_shouldFireFailureBlock && failure) { 142 | failure(nil, _failureErrorToReturn); 143 | } 144 | 145 | return nil; 146 | } 147 | 148 | @end 149 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/Mocks/MockFTGooglePlacesAPISearchResultItemSubclass.h: -------------------------------------------------------------------------------- 1 | // 2 | // MockFTGooglePlacesAPISearchResultItemSubclass.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 05/12/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPISearchResultItem.h" 10 | 11 | @interface MockFTGooglePlacesAPISearchResultItemSubclass : FTGooglePlacesAPISearchResultItem 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/Mocks/MockFTGooglePlacesAPISearchResultItemSubclass.m: -------------------------------------------------------------------------------- 1 | // 2 | // MockFTGooglePlacesAPISearchResultItemSubclass.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 05/12/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "MockFTGooglePlacesAPISearchResultItemSubclass.h" 10 | 11 | @implementation MockFTGooglePlacesAPISearchResultItemSubclass 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/Mocks/MockFTGooglePlacesAPIService.h: -------------------------------------------------------------------------------- 1 | // 2 | // MockFTGooglePlacesAPIService.h 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 05/12/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "FTGooglePlacesAPIService.h" 10 | 11 | @interface MockFTGooglePlacesAPIService : FTGooglePlacesAPIService 12 | 13 | + (FTGooglePlacesAPIService *)singletonInstance; 14 | + (void)setSingletonInstance:(FTGooglePlacesAPIService *)singletonInstance; 15 | 16 | + (void)createDefaultSingletonInstance; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Source/Mocks/MockFTGooglePlacesAPIService.m: -------------------------------------------------------------------------------- 1 | // 2 | // MockFTGooglePlacesAPIService.m 3 | // FTGooglePlacesAPI 4 | // 5 | // Created by Lukas Kukacka on 05/12/13. 6 | // Copyright (c) 2013 Fuerte Int. Ltd. All rights reserved. 7 | // 8 | 9 | #import "MockFTGooglePlacesAPIService.h" 10 | 11 | @implementation MockFTGooglePlacesAPIService 12 | 13 | static FTGooglePlacesAPIService *MockFTGooglePlacesAPIServiceSingletonInstance; 14 | static id MockFTGooglePlacesAPIServiceHTTPRequestOperatioManager; 15 | 16 | + (FTGooglePlacesAPIService *)sharedService 17 | { 18 | return [[self class] singletonInstance]; 19 | } 20 | 21 | #pragma mark - Helper methods 22 | 23 | + (FTGooglePlacesAPIService *)singletonInstance 24 | { 25 | return MockFTGooglePlacesAPIServiceSingletonInstance; 26 | } 27 | 28 | + (void)setSingletonInstance:(FTGooglePlacesAPIService *)singletonInstance 29 | { 30 | MockFTGooglePlacesAPIServiceSingletonInstance = singletonInstance; 31 | } 32 | 33 | + (void)createDefaultSingletonInstance 34 | { 35 | MockFTGooglePlacesAPIServiceSingletonInstance = [[FTGooglePlacesAPIService alloc] init]; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/TestData/FTGooglePlacesAPIResponse-test2-NoResults.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug_info" : [], 3 | "html_attributions" : [], 4 | "results" : [], 5 | "status" : "ZERO_RESULTS" 6 | } -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/TestData/FTGooglePlacesAPIResponse-test3-InvalidRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug_info" : [], 3 | "html_attributions" : [], 4 | "results" : [], 5 | "status" : "INVALID_REQUEST" 6 | } -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/TestData/FTGooglePlacesAPIResponse-test4-RequestDenied.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug_info" : [], 3 | "html_attributions" : [], 4 | "results" : [], 5 | "status" : "REQUEST_DENIED" 6 | } -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Thirdparty/OCMock/OCMock/NSNotificationCenter+OCMAdditions.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id$ 3 | // Copyright (c) 2009 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @class OCMockObserver; 9 | 10 | 11 | @interface NSNotificationCenter(OCMAdditions) 12 | 13 | - (void)addMockObserver:(OCMockObserver *)notificationObserver name:(NSString *)notificationName object:(id)notificationSender; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Thirdparty/OCMock/OCMock/OCMArg.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id$ 3 | // Copyright (c) 2009-2013 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @interface OCMArg : NSObject 9 | 10 | // constraining arguments 11 | 12 | + (id)any; 13 | + (SEL)anySelector; 14 | + (void *)anyPointer; 15 | + (id)isNil; 16 | + (id)isNotNil; 17 | + (id)isNotEqual:(id)value; 18 | + (id)checkWithSelector:(SEL)selector onObject:(id)anObject; 19 | #if NS_BLOCKS_AVAILABLE 20 | + (id)checkWithBlock:(BOOL (^)(id obj))block; 21 | #endif 22 | 23 | // manipulating arguments 24 | 25 | + (id *)setTo:(id)value; 26 | + (void *)setToValue:(NSValue *)value; 27 | 28 | // internal use only 29 | 30 | + (id)resolveSpecialValues:(NSValue *)value; 31 | 32 | @end 33 | 34 | #define OCMOCK_ANY [OCMArg any] 35 | #define OCMOCK_VALUE(variable) [NSValue value:&variable withObjCType:@encode(__typeof__(variable))] 36 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Thirdparty/OCMock/OCMock/OCMConstraint.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id$ 3 | // Copyright (c) 2007-2010 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | 9 | @interface OCMConstraint : NSObject 10 | 11 | + (id)constraint; 12 | - (BOOL)evaluate:(id)value; 13 | 14 | // if you are looking for any, isNil, etc, they have moved to OCMArg 15 | 16 | // try to use [OCMArg checkWith...] instead of the constraintWith... methods below 17 | 18 | + (id)constraintWithSelector:(SEL)aSelector onObject:(id)anObject; 19 | + (id)constraintWithSelector:(SEL)aSelector onObject:(id)anObject withValue:(id)aValue; 20 | 21 | 22 | @end 23 | 24 | @interface OCMAnyConstraint : OCMConstraint 25 | @end 26 | 27 | @interface OCMIsNilConstraint : OCMConstraint 28 | @end 29 | 30 | @interface OCMIsNotNilConstraint : OCMConstraint 31 | @end 32 | 33 | @interface OCMIsNotEqualConstraint : OCMConstraint 34 | { 35 | @public 36 | id testValue; 37 | } 38 | 39 | @end 40 | 41 | @interface OCMInvocationConstraint : OCMConstraint 42 | { 43 | @public 44 | NSInvocation *invocation; 45 | } 46 | 47 | @end 48 | 49 | #if NS_BLOCKS_AVAILABLE 50 | 51 | @interface OCMBlockConstraint : OCMConstraint 52 | { 53 | BOOL (^block)(id); 54 | } 55 | 56 | - (id)initWithConstraintBlock:(BOOL (^)(id))block; 57 | 58 | @end 59 | 60 | #endif 61 | 62 | 63 | #define CONSTRAINT(aSelector) [OCMConstraint constraintWithSelector:aSelector onObject:self] 64 | #define CONSTRAINTV(aSelector, aValue) [OCMConstraint constraintWithSelector:aSelector onObject:self withValue:(aValue)] 65 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Thirdparty/OCMock/OCMock/OCMock.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id$ 3 | // Copyright (c) 2004-2008 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Thirdparty/OCMock/OCMock/OCMockObject.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id$ 3 | // Copyright (c) 2004-2008 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @interface OCMockObject : NSProxy 9 | { 10 | BOOL isNice; 11 | BOOL expectationOrderMatters; 12 | NSMutableArray *recorders; 13 | NSMutableArray *expectations; 14 | NSMutableArray *rejections; 15 | NSMutableArray *exceptions; 16 | } 17 | 18 | + (id)mockForClass:(Class)aClass; 19 | + (id)mockForProtocol:(Protocol *)aProtocol; 20 | + (id)partialMockForObject:(NSObject *)anObject; 21 | 22 | + (id)niceMockForClass:(Class)aClass; 23 | + (id)niceMockForProtocol:(Protocol *)aProtocol; 24 | 25 | + (id)observerMock; 26 | 27 | - (id)init; 28 | 29 | - (void)setExpectationOrderMatters:(BOOL)flag; 30 | 31 | - (id)stub; 32 | - (id)expect; 33 | - (id)reject; 34 | 35 | - (void)verify; 36 | 37 | - (void)stopMocking; 38 | 39 | // internal use only 40 | 41 | - (id)getNewRecorder; 42 | - (BOOL)handleInvocation:(NSInvocation *)anInvocation; 43 | - (void)handleUnRecordedInvocation:(NSInvocation *)anInvocation; 44 | - (BOOL)handleSelector:(SEL)sel; 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Thirdparty/OCMock/OCMock/OCMockRecorder.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id$ 3 | // Copyright (c) 2004-2013 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @interface OCMockRecorder : NSProxy 9 | { 10 | id signatureResolver; 11 | BOOL recordedAsClassMethod; 12 | BOOL ignoreNonObjectArgs; 13 | NSInvocation *recordedInvocation; 14 | NSMutableArray *invocationHandlers; 15 | } 16 | 17 | - (id)initWithSignatureResolver:(id)anObject; 18 | 19 | - (BOOL)matchesSelector:(SEL)sel; 20 | - (BOOL)matchesInvocation:(NSInvocation *)anInvocation; 21 | - (void)releaseInvocation; 22 | 23 | - (id)andReturn:(id)anObject; 24 | - (id)andReturnValue:(NSValue *)aValue; 25 | - (id)andThrow:(NSException *)anException; 26 | - (id)andPost:(NSNotification *)aNotification; 27 | - (id)andCall:(SEL)selector onObject:(id)anObject; 28 | #if NS_BLOCKS_AVAILABLE 29 | - (id)andDo:(void (^)(NSInvocation *))block; 30 | #endif 31 | - (id)andForwardToRealObject; 32 | 33 | - (id)classMethod; 34 | - (id)ignoringNonObjectArgs; 35 | 36 | - (NSArray *)invocationHandlers; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/Thirdparty/OCMock/libOCMock.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manGoweb/FTGooglePlacesAPI/b35b1d15121694df75249080e44621722f98d937/XcodeProject/FTGooglePlacesAPILogicTests/Thirdparty/OCMock/libOCMock.a -------------------------------------------------------------------------------- /XcodeProject/FTGooglePlacesAPILogicTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | --------------------------------------------------------------------------------