├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── GooglePlacesAutocomplete.podspec ├── GooglePlacesAutocomplete.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── GooglePlacesAutocomplete.xcscheme ├── GooglePlacesAutocomplete.xcworkspace └── contents.xcworkspacedata ├── GooglePlacesAutocomplete ├── GooglePlacesAutocomplete.h ├── GooglePlacesAutocomplete.swift ├── GooglePlacesAutocomplete.xib ├── Images.xcassets │ └── PoweredByGoogle.imageset │ │ ├── Contents.json │ │ ├── powered-by-google-on-white.png │ │ └── powered-by-google-on-white@2x.png └── Info.plist ├── GooglePlacesAutocompleteExample ├── GooglePlacesAutocompleteExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── GooglePlacesAutocompleteExample.xcscheme ├── GooglePlacesAutocompleteExample │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift └── GooglePlacesAutocompleteExampleTests │ ├── GooglePlaceDetailsRequestTests.swift │ ├── GooglePlacesAutocompleteExample-Bridging-Header.h │ ├── GooglePlacesAutocompleteExampleTests.swift │ ├── Info.plist │ ├── LocationBiasRequestTests.swift │ └── ReferenceImages_64 │ └── GooglePlacesAutocompleteExampleTests.GooglePlacesAutocompleteTests │ ├── testGooglePlacesAutocomplete_search@2x.png │ └── testGooglePlacesAutocomplete_view@2x.png ├── GooglePlacesAutocompleteTests └── Info.plist ├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods ├── FBSnapshotTestCase │ ├── FBSnapshotTestCase │ │ ├── Categories │ │ │ ├── UIImage+Compare.h │ │ │ ├── UIImage+Compare.m │ │ │ ├── UIImage+Diff.h │ │ │ ├── UIImage+Diff.m │ │ │ ├── UIImage+Snapshot.h │ │ │ └── UIImage+Snapshot.m │ │ ├── FBSnapshotTestCase.h │ │ ├── FBSnapshotTestCase.m │ │ ├── FBSnapshotTestCasePlatform.h │ │ ├── FBSnapshotTestCasePlatform.m │ │ ├── FBSnapshotTestController.h │ │ ├── FBSnapshotTestController.m │ │ └── SwiftSupport.swift │ ├── LICENSE │ └── README.md ├── Headers │ └── Private │ │ ├── FBSnapshotTestCase │ │ ├── FBSnapshotTestCase.h │ │ ├── FBSnapshotTestCasePlatform.h │ │ ├── FBSnapshotTestController.h │ │ ├── UIImage+Compare.h │ │ ├── UIImage+Diff.h │ │ └── UIImage+Snapshot.h │ │ └── OHHTTPStubs │ │ ├── Compatibility.h │ │ ├── OHHTTPStubs.h │ │ ├── OHHTTPStubsResponse+JSON.h │ │ ├── OHHTTPStubsResponse.h │ │ └── OHPathHelpers.h ├── Local Podspecs │ └── FBSnapshotTestCase.podspec.json ├── Manifest.lock ├── OHHTTPStubs │ ├── LICENSE │ ├── OHHTTPStubs │ │ └── Sources │ │ │ ├── Compatibility.h │ │ │ ├── JSON │ │ │ ├── OHHTTPStubsResponse+JSON.h │ │ │ └── OHHTTPStubsResponse+JSON.m │ │ │ ├── NSURLSession │ │ │ └── OHHTTPStubs+NSURLSessionConfiguration.m │ │ │ ├── OHHTTPStubs.h │ │ │ ├── OHHTTPStubs.m │ │ │ ├── OHHTTPStubsResponse.h │ │ │ ├── OHHTTPStubsResponse.m │ │ │ └── OHPathHelpers │ │ │ ├── OHPathHelpers.h │ │ │ └── OHPathHelpers.m │ └── README.md ├── Pods.xcodeproj │ └── project.pbxproj └── Target Support Files │ ├── FBSnapshotTestCase │ ├── FBSnapshotTestCase-dummy.m │ ├── FBSnapshotTestCase-prefix.pch │ ├── FBSnapshotTestCase-umbrella.h │ ├── FBSnapshotTestCase.modulemap │ ├── FBSnapshotTestCase.xcconfig │ └── Info.plist │ ├── OHHTTPStubs │ ├── Info.plist │ ├── OHHTTPStubs-dummy.m │ ├── OHHTTPStubs-prefix.pch │ ├── OHHTTPStubs-umbrella.h │ ├── OHHTTPStubs.modulemap │ └── OHHTTPStubs.xcconfig │ └── Pods-GooglePlacesAutocompleteExampleTests │ ├── Info.plist │ ├── Pods-GooglePlacesAutocompleteExampleTests-acknowledgements.markdown │ ├── Pods-GooglePlacesAutocompleteExampleTests-acknowledgements.plist │ ├── Pods-GooglePlacesAutocompleteExampleTests-dummy.m │ ├── Pods-GooglePlacesAutocompleteExampleTests-frameworks.sh │ ├── Pods-GooglePlacesAutocompleteExampleTests-resources.sh │ ├── Pods-GooglePlacesAutocompleteExampleTests-umbrella.h │ ├── Pods-GooglePlacesAutocompleteExampleTests.debug.xcconfig │ ├── Pods-GooglePlacesAutocompleteExampleTests.modulemap │ └── Pods-GooglePlacesAutocompleteExampleTests.release.xcconfig ├── README.md ├── Rakefile └── Screenshots ├── search.png ├── style.png └── view.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Exclude personal Xcode user settings 4 | xcuserdata/ 5 | 6 | build/ 7 | *.pbxuser 8 | !default.pbxuser 9 | *.mode1v3 10 | !default.mode1v3 11 | *.mode2v3 12 | !default.mode2v3 13 | *.perspectivev3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xccheckout 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | vendor/ 22 | .bundle 23 | *.xcscmblueprint -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7 3 | sudo: false 4 | 5 | cache: 6 | directories: 7 | - vendor/bundle 8 | 9 | before_install: 10 | - bundle install --path=vendor/bundle --binstubs=vendor/bin 11 | 12 | script: 13 | - bundle exec rake test 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 0.3.0 - 2015-06-11 4 | 5 | - Now compatible with XCode 6.2+ and Swift 1.2 6 | 7 | ## 0.2.1 - 2015-05-26 8 | 9 | - Allow programmatic clearing of search bar 10 | - Add proxy access to container navigationItem for easier styling 11 | 12 | ## 0.2.0 - 2015-04-02 13 | 14 | - Change close button style 15 | - Use `place_id` for predictions now that `id` is deprecated 16 | - Allow Place objects to be created outside the library 17 | - Fix bug by renaming 'type' parameter to 'types' 18 | - Add facility for getting details from Place Details API [Fixes #5] 19 | 20 | ## 0.1.0 - 2015-02-16 21 | 22 | Initial release -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake' 4 | gem 'cocoapods', '~> 0.39.0' 5 | gem 'xcpretty' 6 | gem 'xcjobs' 7 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (4.2.4) 5 | i18n (~> 0.7) 6 | json (~> 1.7, >= 1.7.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | claide (0.9.1) 11 | cocoapods (0.39.0) 12 | activesupport (>= 4.0.2) 13 | claide (~> 0.9.1) 14 | cocoapods-core (= 0.39.0) 15 | cocoapods-downloader (~> 0.9.3) 16 | cocoapods-plugins (~> 0.4.2) 17 | cocoapods-search (~> 0.1.0) 18 | cocoapods-stats (~> 0.6.2) 19 | cocoapods-trunk (~> 0.6.4) 20 | cocoapods-try (~> 0.5.1) 21 | colored (~> 1.2) 22 | escape (~> 0.0.4) 23 | molinillo (~> 0.4.0) 24 | nap (~> 1.0) 25 | xcodeproj (~> 0.28.2) 26 | cocoapods-core (0.39.0) 27 | activesupport (>= 4.0.2) 28 | fuzzy_match (~> 2.0.4) 29 | nap (~> 1.0) 30 | cocoapods-downloader (0.9.3) 31 | cocoapods-plugins (0.4.2) 32 | nap 33 | cocoapods-search (0.1.0) 34 | cocoapods-stats (0.6.2) 35 | cocoapods-trunk (0.6.4) 36 | nap (>= 0.8, < 2.0) 37 | netrc (= 0.7.8) 38 | cocoapods-try (0.5.1) 39 | colored (1.2) 40 | escape (0.0.4) 41 | fuzzy_match (2.0.4) 42 | i18n (0.7.0) 43 | json (1.8.3) 44 | minitest (5.8.1) 45 | molinillo (0.4.0) 46 | nap (1.0.0) 47 | netrc (0.7.8) 48 | rake (10.4.2) 49 | thread_safe (0.3.5) 50 | tzinfo (1.2.2) 51 | thread_safe (~> 0.1) 52 | xcjobs (0.0.7) 53 | xcodeproj (0.28.2) 54 | activesupport (>= 3) 55 | claide (~> 0.9.1) 56 | colored (~> 1.2) 57 | xcpretty (0.1.7) 58 | 59 | PLATFORMS 60 | ruby 61 | 62 | DEPENDENCIES 63 | cocoapods (~> 0.39.0) 64 | rake 65 | xcjobs 66 | xcpretty 67 | 68 | BUNDLED WITH 69 | 1.10.5 70 | -------------------------------------------------------------------------------- /GooglePlacesAutocomplete.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = "GooglePlacesAutocomplete" 3 | spec.version = "0.3.0" 4 | spec.summary = "Simple Google Places autocompleting address entry view" 5 | spec.homepage = "https://github.com/watsonbox/ios_google_places_autocomplete" 6 | spec.screenshots = "https://raw.githubusercontent.com/watsonbox/ios_google_places_autocomplete/master/Screenshots/view.png", "https://raw.githubusercontent.com/watsonbox/ios_google_places_autocomplete/master/Screenshots/search.png" 7 | spec.license = 'MIT' 8 | spec.author = { "Howard Wilson" => "howard@watsonbox.net" } 9 | spec.source = { :git => "https://github.com/watsonbox/ios_google_places_autocomplete.git", :tag => spec.version.to_s } 10 | spec.platform = :ios, '8.0' 11 | spec.source_files = 'GooglePlacesAutocomplete/*.{h,swift}' 12 | spec.resources = 'GooglePlacesAutocomplete/*.{xcassets,xib}' 13 | spec.requires_arc = true 14 | spec.frameworks = 'UIKit' 15 | end 16 | -------------------------------------------------------------------------------- /GooglePlacesAutocomplete.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GooglePlacesAutocomplete.xcodeproj/xcshareddata/xcschemes/GooglePlacesAutocomplete.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /GooglePlacesAutocomplete.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /GooglePlacesAutocomplete/GooglePlacesAutocomplete.h: -------------------------------------------------------------------------------- 1 | // 2 | // GooglePlacesAutocomplete.h 3 | // GooglePlacesAutocomplete 4 | // 5 | // Created by Howard Wilson on 13/02/2015. 6 | // Copyright (c) 2015 Howard Wilson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for GooglePlacesAutocomplete. 12 | FOUNDATION_EXPORT double GooglePlacesAutocompleteVersionNumber; 13 | 14 | //! Project version string for GooglePlacesAutocomplete. 15 | FOUNDATION_EXPORT const unsigned char GooglePlacesAutocompleteVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /GooglePlacesAutocomplete/GooglePlacesAutocomplete.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GooglePlacesAutocomplete.swift 3 | // GooglePlacesAutocomplete 4 | // 5 | // Created by Howard Wilson on 10/02/2015. 6 | // Copyright (c) 2015 Howard Wilson. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public let ErrorDomain: String! = "GooglePlacesAutocompleteErrorDomain" 12 | 13 | public struct LocationBias { 14 | public let latitude: Double 15 | public let longitude: Double 16 | public let radius: Int 17 | 18 | public init(latitude: Double = 0, longitude: Double = 0, radius: Int = 20000000) { 19 | self.latitude = latitude 20 | self.longitude = longitude 21 | self.radius = radius 22 | } 23 | 24 | public var location: String { 25 | return "\(latitude),\(longitude)" 26 | } 27 | } 28 | 29 | public enum PlaceType: CustomStringConvertible { 30 | case All 31 | case Geocode 32 | case Address 33 | case Establishment 34 | case Regions 35 | case Cities 36 | 37 | public var description : String { 38 | switch self { 39 | case .All: return "" 40 | case .Geocode: return "geocode" 41 | case .Address: return "address" 42 | case .Establishment: return "establishment" 43 | case .Regions: return "(regions)" 44 | case .Cities: return "(cities)" 45 | } 46 | } 47 | } 48 | 49 | public class Place: NSObject { 50 | public let id: String 51 | public let desc: String 52 | public var apiKey: String? 53 | 54 | override public var description: String { 55 | get { return desc } 56 | } 57 | 58 | public init(id: String, description: String) { 59 | self.id = id 60 | self.desc = description 61 | } 62 | 63 | public convenience init(prediction: [String: AnyObject], apiKey: String?) { 64 | self.init( 65 | id: prediction["place_id"] as! String, 66 | description: prediction["description"] as! String 67 | ) 68 | 69 | self.apiKey = apiKey 70 | } 71 | 72 | /** 73 | Call Google Place Details API to get detailed information for this place 74 | 75 | Requires that Place#apiKey be set 76 | 77 | - parameter result: Callback on successful completion with detailed place information 78 | */ 79 | public func getDetails(result: PlaceDetails -> ()) { 80 | GooglePlaceDetailsRequest(place: self).request(result) 81 | } 82 | } 83 | 84 | public class PlaceDetails: CustomStringConvertible { 85 | public let name: String 86 | public let latitude: Double 87 | public let longitude: Double 88 | public let raw: [String: AnyObject] 89 | 90 | public init(json: [String: AnyObject]) { 91 | let result = json["result"] as! [String: AnyObject] 92 | let geometry = result["geometry"] as! [String: AnyObject] 93 | let location = geometry["location"] as! [String: AnyObject] 94 | 95 | self.name = result["name"] as! String 96 | self.latitude = location["lat"] as! Double 97 | self.longitude = location["lng"] as! Double 98 | self.raw = json 99 | } 100 | 101 | public var description: String { 102 | return "PlaceDetails: \(name) (\(latitude), \(longitude))" 103 | } 104 | } 105 | 106 | @objc public protocol GooglePlacesAutocompleteDelegate { 107 | optional func placesFound(places: [Place]) 108 | optional func placeSelected(place: Place) 109 | optional func placeViewClosed() 110 | } 111 | 112 | // MARK: - GooglePlacesAutocomplete 113 | public class GooglePlacesAutocomplete: UINavigationController { 114 | public var gpaViewController: GooglePlacesAutocompleteContainer! 115 | public var closeButton: UIBarButtonItem! 116 | 117 | // Proxy access to container navigationItem 118 | public override var navigationItem: UINavigationItem { 119 | get { return gpaViewController.navigationItem } 120 | } 121 | 122 | public var placeDelegate: GooglePlacesAutocompleteDelegate? { 123 | get { return gpaViewController.delegate } 124 | set { gpaViewController.delegate = newValue } 125 | } 126 | 127 | public var locationBias: LocationBias? { 128 | get { return gpaViewController.locationBias } 129 | set { gpaViewController.locationBias = newValue } 130 | } 131 | 132 | public convenience init(apiKey: String, placeType: PlaceType = .All) { 133 | let gpaViewController = GooglePlacesAutocompleteContainer( 134 | apiKey: apiKey, 135 | placeType: placeType 136 | ) 137 | 138 | self.init(rootViewController: gpaViewController) 139 | self.gpaViewController = gpaViewController 140 | 141 | closeButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Stop, target: self, action: "close") 142 | closeButton.style = UIBarButtonItemStyle.Done 143 | 144 | gpaViewController.navigationItem.leftBarButtonItem = closeButton 145 | gpaViewController.navigationItem.title = "Enter Address" 146 | } 147 | 148 | func close() { 149 | placeDelegate?.placeViewClosed?() 150 | } 151 | 152 | public func reset() { 153 | gpaViewController.searchBar.text = "" 154 | gpaViewController.searchBar(gpaViewController.searchBar, textDidChange: "") 155 | } 156 | } 157 | 158 | // MARK: - GooglePlacesAutocompleteContainer 159 | public class GooglePlacesAutocompleteContainer: UIViewController { 160 | @IBOutlet public weak var searchBar: UISearchBar! 161 | @IBOutlet weak var tableView: UITableView! 162 | @IBOutlet weak var topConstraint: NSLayoutConstraint! 163 | 164 | var delegate: GooglePlacesAutocompleteDelegate? 165 | var apiKey: String? 166 | var places = [Place]() 167 | var placeType: PlaceType = .All 168 | var locationBias: LocationBias? 169 | 170 | convenience init(apiKey: String, placeType: PlaceType = .All) { 171 | let bundle = NSBundle(forClass: GooglePlacesAutocompleteContainer.self) 172 | 173 | self.init(nibName: "GooglePlacesAutocomplete", bundle: bundle) 174 | self.apiKey = apiKey 175 | self.placeType = placeType 176 | } 177 | 178 | deinit { 179 | NSNotificationCenter.defaultCenter().removeObserver(self) 180 | } 181 | 182 | override public func viewWillLayoutSubviews() { 183 | topConstraint.constant = topLayoutGuide.length 184 | } 185 | 186 | override public func viewDidLoad() { 187 | super.viewDidLoad() 188 | 189 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil) 190 | NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil) 191 | 192 | searchBar.becomeFirstResponder() 193 | tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell") 194 | } 195 | 196 | func keyboardWasShown(notification: NSNotification) { 197 | if isViewLoaded() && view.window != nil { 198 | let info: Dictionary = notification.userInfo! 199 | let keyboardSize: CGSize = (info[UIKeyboardFrameBeginUserInfoKey]?.CGRectValue.size)! 200 | let contentInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0) 201 | 202 | tableView.contentInset = contentInsets; 203 | tableView.scrollIndicatorInsets = contentInsets; 204 | } 205 | } 206 | 207 | func keyboardWillBeHidden(notification: NSNotification) { 208 | if isViewLoaded() && view.window != nil { 209 | self.tableView.contentInset = UIEdgeInsetsZero 210 | self.tableView.scrollIndicatorInsets = UIEdgeInsetsZero 211 | } 212 | } 213 | } 214 | 215 | // MARK: - GooglePlacesAutocompleteContainer (UITableViewDataSource / UITableViewDelegate) 216 | extension GooglePlacesAutocompleteContainer: UITableViewDataSource, UITableViewDelegate { 217 | public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 218 | return places.count 219 | } 220 | 221 | public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 222 | let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 223 | 224 | // Get the corresponding candy from our candies array 225 | let place = self.places[indexPath.row] 226 | 227 | // Configure the cell 228 | cell.textLabel!.text = place.description 229 | cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator 230 | 231 | return cell 232 | } 233 | 234 | public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 235 | delegate?.placeSelected?(self.places[indexPath.row]) 236 | } 237 | } 238 | 239 | // MARK: - GooglePlacesAutocompleteContainer (UISearchBarDelegate) 240 | extension GooglePlacesAutocompleteContainer: UISearchBarDelegate { 241 | public func searchBar(searchBar: UISearchBar, textDidChange searchText: String) { 242 | if (searchText == "") { 243 | self.places = [] 244 | tableView.hidden = true 245 | } else { 246 | getPlaces(searchText) 247 | } 248 | } 249 | 250 | /** 251 | Call the Google Places API and update the view with results. 252 | 253 | - parameter searchString: The search query 254 | */ 255 | 256 | private func getPlaces(searchString: String) { 257 | var params = [ 258 | "input": searchString, 259 | "types": placeType.description, 260 | "key": apiKey ?? "" 261 | ] 262 | 263 | if let bias = locationBias { 264 | params["location"] = bias.location 265 | params["radius"] = bias.radius.description 266 | } 267 | 268 | if (searchString == ""){ 269 | return 270 | } 271 | 272 | GooglePlacesRequestHelpers.doRequest( 273 | "https://maps.googleapis.com/maps/api/place/autocomplete/json", 274 | params: params 275 | ) { json, error in 276 | if let json = json{ 277 | if let predictions = json["predictions"] as? Array<[String: AnyObject]> { 278 | self.places = predictions.map { (prediction: [String: AnyObject]) -> Place in 279 | return Place(prediction: prediction, apiKey: self.apiKey) 280 | } 281 | self.tableView.reloadData() 282 | self.tableView.hidden = false 283 | self.delegate?.placesFound?(self.places) 284 | } 285 | } 286 | } 287 | } 288 | } 289 | 290 | // MARK: - GooglePlaceDetailsRequest 291 | class GooglePlaceDetailsRequest { 292 | let place: Place 293 | 294 | init(place: Place) { 295 | self.place = place 296 | } 297 | 298 | func request(result: PlaceDetails -> ()) { 299 | GooglePlacesRequestHelpers.doRequest( 300 | "https://maps.googleapis.com/maps/api/place/details/json", 301 | params: [ 302 | "placeid": place.id, 303 | "key": place.apiKey ?? "" 304 | ] 305 | ) { json, error in 306 | if let json = json as? [String: AnyObject] { 307 | result(PlaceDetails(json: json)) 308 | } 309 | if let error = error { 310 | // TODO: We should probably pass back details of the error 311 | print("Error fetching google place details: \(error)") 312 | } 313 | } 314 | } 315 | } 316 | 317 | // MARK: - GooglePlacesRequestHelpers 318 | class GooglePlacesRequestHelpers { 319 | /** 320 | Build a query string from a dictionary 321 | 322 | - parameter parameters: Dictionary of query string parameters 323 | - returns: The properly escaped query string 324 | */ 325 | private class func query(parameters: [String: AnyObject]) -> String { 326 | var components: [(String, String)] = [] 327 | for key in Array(parameters.keys).sort(<) { 328 | let value: AnyObject! = parameters[key] 329 | components += [(escape(key), escape("\(value)"))] 330 | } 331 | 332 | return (components.map{"\($0)=\($1)"} as [String]).joinWithSeparator("&") 333 | } 334 | 335 | private class func escape(string: String) -> String { 336 | let legalURLCharactersToBeEscaped: CFStringRef = ":/?&=;+!@#$()',*" 337 | return CFURLCreateStringByAddingPercentEscapes(nil, string, nil, legalURLCharactersToBeEscaped, CFStringBuiltInEncodings.UTF8.rawValue) as String 338 | } 339 | 340 | private class func doRequest(url: String, params: [String: String], completion: (NSDictionary?,NSError?) -> ()) { 341 | let request = NSMutableURLRequest( 342 | URL: NSURL(string: "\(url)?\(query(params))")! 343 | ) 344 | 345 | let session = NSURLSession.sharedSession() 346 | let task = session.dataTaskWithRequest(request) { data, response, error in 347 | self.handleResponse(data, response: response as? NSHTTPURLResponse, error: error, completion: completion) 348 | } 349 | 350 | task.resume() 351 | } 352 | 353 | private class func handleResponse(data: NSData!, response: NSHTTPURLResponse!, error: NSError!, completion: (NSDictionary?, NSError?) -> ()) { 354 | 355 | // Always return on the main thread... 356 | let done: ((NSDictionary?, NSError?) -> Void) = {(json, error) in 357 | dispatch_async(dispatch_get_main_queue(), { 358 | UIApplication.sharedApplication().networkActivityIndicatorVisible = false 359 | completion(json,error) 360 | }) 361 | } 362 | 363 | if let error = error { 364 | print("GooglePlaces Error: \(error.localizedDescription)") 365 | done(nil,error) 366 | return 367 | } 368 | 369 | if response == nil { 370 | print("GooglePlaces Error: No response from API") 371 | let error = NSError(domain: ErrorDomain, code: 1001, userInfo: [NSLocalizedDescriptionKey:"No response from API"]) 372 | done(nil,error) 373 | return 374 | } 375 | 376 | if response.statusCode != 200 { 377 | print("GooglePlaces Error: Invalid status code \(response.statusCode) from API") 378 | let error = NSError(domain: ErrorDomain, code: response.statusCode, userInfo: [NSLocalizedDescriptionKey:"Invalid status code"]) 379 | done(nil,error) 380 | return 381 | } 382 | 383 | let json: NSDictionary? 384 | do { 385 | json = try NSJSONSerialization.JSONObjectWithData( 386 | data, 387 | options: NSJSONReadingOptions.MutableContainers) as? NSDictionary 388 | } catch { 389 | print("Serialisation error") 390 | let serialisationError = NSError(domain: ErrorDomain, code: 1002, userInfo: [NSLocalizedDescriptionKey:"Serialization error"]) 391 | done(nil,serialisationError) 392 | return 393 | } 394 | 395 | if let status = json?["status"] as? String { 396 | if status != "OK" { 397 | print("GooglePlaces API Error: \(status)") 398 | let error = NSError(domain: ErrorDomain, code: 1002, userInfo: [NSLocalizedDescriptionKey:status]) 399 | done(nil,error) 400 | return 401 | } 402 | } 403 | 404 | done(json,nil) 405 | 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /GooglePlacesAutocomplete/GooglePlacesAutocomplete.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /GooglePlacesAutocomplete/Images.xcassets/PoweredByGoogle.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "powered-by-google-on-white.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "powered-by-google-on-white@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /GooglePlacesAutocomplete/Images.xcassets/PoweredByGoogle.imageset/powered-by-google-on-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/watsonbox/ios_google_places_autocomplete/3b24a37eca923c5d92253122f26a2cd41f10ec5a/GooglePlacesAutocomplete/Images.xcassets/PoweredByGoogle.imageset/powered-by-google-on-white.png -------------------------------------------------------------------------------- /GooglePlacesAutocomplete/Images.xcassets/PoweredByGoogle.imageset/powered-by-google-on-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/watsonbox/ios_google_places_autocomplete/3b24a37eca923c5d92253122f26a2cd41f10ec5a/GooglePlacesAutocomplete/Images.xcassets/PoweredByGoogle.imageset/powered-by-google-on-white@2x.png -------------------------------------------------------------------------------- /GooglePlacesAutocomplete/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | net.watsonbox.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample.xcodeproj/xcshareddata/xcschemes/GooglePlacesAutocompleteExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 93 | 94 | 95 | 96 | 97 | 98 | 104 | 106 | 112 | 113 | 114 | 115 | 117 | 118 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GooglePlacesAutocompleteExample 4 | // 5 | // Created by Howard Wilson on 15/02/2015. 6 | // Copyright (c) 2015 Howard Wilson. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | var didFinishLaunching: (Void -> Void)? = nil 17 | 18 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 19 | 20 | didFinishLaunching?() 21 | 22 | // Override point for customization after application launch. 23 | return true 24 | } 25 | 26 | func applicationWillResignActive(application: UIApplication) { 27 | // 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. 28 | // 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. 29 | } 30 | 31 | func applicationDidEnterBackground(application: UIApplication) { 32 | // 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. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | func applicationWillEnterForeground(application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationDidBecomeActive(application: UIApplication) { 41 | // 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. 42 | } 43 | 44 | func applicationWillTerminate(application: UIApplication) { 45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 46 | } 47 | 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // GooglePlacesAutocompleteExample 4 | // 5 | // Created by Howard Wilson on 15/02/2015. 6 | // Copyright (c) 2015 Howard Wilson. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import GooglePlacesAutocomplete 11 | 12 | class ViewController: UIViewController { 13 | let gpaViewController = GooglePlacesAutocomplete( 14 | apiKey: "[YOUR GOOGLE PLACES API KEY]", 15 | placeType: .Address 16 | ) 17 | 18 | override func viewDidAppear(animated: Bool) { 19 | super.viewDidAppear(animated) 20 | 21 | gpaViewController.placeDelegate = self 22 | 23 | presentViewController(gpaViewController, animated: true, completion: nil) 24 | } 25 | } 26 | 27 | extension ViewController: GooglePlacesAutocompleteDelegate { 28 | func placeSelected(place: Place) { 29 | print(place.description) 30 | 31 | place.getDetails { details in 32 | print(details) 33 | } 34 | } 35 | 36 | func placeViewClosed() { 37 | dismissViewControllerAnimated(true, completion: nil) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExampleTests/GooglePlaceDetailsRequestTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GooglePlaceDetailsRequestTests.swift 3 | // GooglePlacesAutocompleteExample 4 | // 5 | // Created by Howard Wilson on 25/03/2015. 6 | // Copyright (c) 2015 Howard Wilson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import GooglePlacesAutocomplete 12 | import OHHTTPStubs 13 | 14 | class GooglePlaceDetailsRequestTests: XCTestCase { 15 | let json: [String : AnyObject] = [ 16 | "html_attributions" : [], 17 | "result" : [ 18 | "formatted_address" : "48 Pirrama Road, Pyrmont NSW, Australia", 19 | "formatted_phone_number" : "(02) 9374 4000", 20 | "geometry" : [ 21 | "location" : [ 22 | "lat" : -33.8669710, 23 | "lng" : 151.1958750 24 | ] 25 | ], 26 | "icon" : "http://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png", 27 | "id" : "4f89212bf76dde31f092cfc14d7506555d85b5c7", 28 | "name" : "Google Sydney" 29 | // ... 30 | ], 31 | "status" : "OK" 32 | ] 33 | 34 | func testSuccessfulDetailsRequest() { 35 | let place = Place(prediction: ["place_id": "691b237b0322f28988f3ce03e321ff72a12167fd", "description": "Paris, France"], apiKey: "APIKEY") 36 | let expectation = self.expectationWithDescription("Should return details") 37 | 38 | OHHTTPStubs.stubRequestsPassingTest({ (request: NSURLRequest!) -> Bool in 39 | return request.URL!.absoluteString == "https://maps.googleapis.com/maps/api/place/details/json?key=APIKEY&placeid=\(place.id)" 40 | }, withStubResponse: { (request: NSURLRequest!) -> OHHTTPStubsResponse in 41 | return OHHTTPStubsResponse(JSONObject: self.json, statusCode: 200, headers: nil) 42 | }) 43 | 44 | place.getDetails { details in 45 | XCTAssertEqual(details.name, "Google Sydney") 46 | XCTAssertEqual(details.latitude, -33.8669710) 47 | XCTAssertEqual(details.longitude, 151.1958750) 48 | 49 | expectation.fulfill() 50 | } 51 | 52 | self.waitForExpectationsWithTimeout(2.0, handler: nil) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExampleTests/GooglePlacesAutocompleteExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // GooglePlacesAutocompleteExample-Bridging-Header.h 3 | // GooglePlacesAutocompleteExample 4 | // 5 | // Created by Howard Wilson on 16/02/2015. 6 | // Copyright (c) 2015 Howard Wilson. All rights reserved. 7 | // 8 | 9 | #ifndef GooglePlacesAutocompleteExample_GooglePlacesAutocompleteExample_Bridging_Header_h 10 | #define GooglePlacesAutocompleteExample_GooglePlacesAutocompleteExample_Bridging_Header_h 11 | 12 | #import 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExampleTests/GooglePlacesAutocompleteExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GooglePlacesAutocompleteExampleTests.swift 3 | // GooglePlacesAutocompleteExampleTests 4 | // 5 | // Created by Howard Wilson on 15/02/2015. 6 | // Copyright (c) 2015 Howard Wilson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import XCTest 12 | import GooglePlacesAutocomplete 13 | import FBSnapshotTestCase 14 | import OHHTTPStubs 15 | 16 | class GooglePlacesAutocompleteTests: FBSnapshotTestCase, GooglePlacesAutocompleteDelegate { 17 | let gpaViewController = GooglePlacesAutocomplete(apiKey: "APIKEY") 18 | var expectation: XCTestExpectation! 19 | 20 | func testGooglePlacesAutocomplete() { 21 | let json: [String : AnyObject] = ["predictions" : [prediction1, prediction2]] 22 | expectation = self.expectationWithDescription("Should return results") 23 | 24 | OHHTTPStubs.stubRequestsPassingTest({ (request: NSURLRequest!) -> Bool in 25 | return request.URL!.absoluteString == "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=Paris&key=APIKEY&types=" 26 | }, withStubResponse: { (request: NSURLRequest!) -> OHHTTPStubsResponse in 27 | return OHHTTPStubsResponse(JSONObject: json, statusCode: 200, headers: nil) 28 | }) 29 | 30 | self.gpaViewController.placeDelegate = self 31 | 32 | UIApplication.sharedApplication().keyWindow!.rootViewController = UIViewController() 33 | 34 | let rootVC = UIApplication.sharedApplication().keyWindow!.rootViewController! 35 | 36 | rootVC.presentViewController(self.gpaViewController, animated: false, completion: { 37 | self.FBSnapshotVerifyView(self.gpaViewController.view, identifier: "view") 38 | 39 | self.gpaViewController.gpaViewController.searchBar( 40 | self.gpaViewController.gpaViewController.searchBar, 41 | textDidChange: "Paris" 42 | ) 43 | }) 44 | 45 | self.waitForExpectationsWithTimeout(2.0, handler: nil) 46 | } 47 | 48 | func placesFound(places: [Place]) { 49 | self.FBSnapshotVerifyView(self.gpaViewController.view, identifier: "search") 50 | expectation.fulfill() 51 | } 52 | 53 | let prediction1: [String : AnyObject] = [ 54 | "description" : "Paris, France", 55 | "id" : "691b237b0322f28988f3ce03e321ff72a12167fd", 56 | "matched_substrings" : [ 57 | ["length" : 5, "offset" : 0] 58 | ], 59 | "place_id" : "ChIJD7fiBh9u5kcRYJSMaMOCCwQ", 60 | "reference" : "CjQlAAAAbHAcwNAV9grGOKRGKz0czmHc_KsFufZ90X7ZhD0aPhWpyTb8-BQqe0GwWGDdGYzbEhBhFHGRSW6t6U8do2RzgUe0GhRZivpe7tNn7ujO7sWz6Vkv9CNyXg", 61 | "terms" : [ 62 | ["offset" : 0, "value" : "Paris"], 63 | ["offset" : 7, "value" : "France"] 64 | ], 65 | "types" : [ "locality", "political", "geocode" ] 66 | ] 67 | 68 | let prediction2: [String : AnyObject] = [ 69 | "description" : "Paris 17, Paris, France", 70 | "id" : "126ccd7b36db3990466ee234998f25ab92ce88ac", 71 | "matched_substrings" : [ 72 | ["length" : 5, "offset" : 0] 73 | ], 74 | "place_id" : "ChIJVRQP1aJv5kcRUBuUaMOCCwU", 75 | "reference" : "CjQvAAAAR0bndCO53tJbbUDTclTXN6rgKRDEqCmsoYCDq5qpHCnOnhhrtyXmFSwWx-zVvWi0EhD6G6PPrJTOQEazhy5-JFhVGhRND1R7Or4V3lDaHkBcXt98X8u5mw", 76 | "terms" : [ 77 | ["offset" : 0, "value" : "Paris 17"], 78 | ["offset" : 10, "value" : "Paris"], 79 | ["offset" : 17, "value" : "France"] 80 | ], 81 | "types" : [ "sublocality_level_1", "sublocality", "political", "geocode" ] 82 | ] 83 | } 84 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExampleTests/LocationBiasRequestTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GooglePlacesLocationBiasRequestTests.swift 3 | // GooglePlacesAutocompleteExample 4 | // 5 | // Created by Howard Wilson on 29/06/2015. 6 | // Copyright (c) 2015 Howard Wilson. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | import GooglePlacesAutocomplete 12 | import OHHTTPStubs 13 | 14 | class LocationBiasRequestTests: XCTestCase, GooglePlacesAutocompleteDelegate { 15 | let gpaViewController = GooglePlacesAutocomplete(apiKey: "APIKEY") 16 | var expectation: XCTestExpectation! 17 | 18 | func testLocationBiasRequest() { 19 | expectation = self.expectationWithDescription("Should return biased results") 20 | 21 | OHHTTPStubs.stubRequestsPassingTest({ (request: NSURLRequest!) -> Bool in 22 | return request.URL!.absoluteString == "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=Paris&key=APIKEY&location=48.8534275%2C2.35827879999999&radius=1000&types=" 23 | }, withStubResponse: { (request: NSURLRequest!) -> OHHTTPStubsResponse in 24 | return OHHTTPStubsResponse( 25 | JSONObject: ["predictions" : [[ 26 | "description" : "Paris, France", 27 | "place_id" : "ChIJD7fiBh9u5kcRYJSMaMOCCwQ" 28 | ]]], 29 | statusCode: 200, headers: nil) 30 | }) 31 | 32 | self.gpaViewController.placeDelegate = self 33 | self.gpaViewController.locationBias = LocationBias(latitude: 48.8534275, longitude: 2.3582787999999937, radius: 1000) 34 | 35 | UIApplication.sharedApplication().keyWindow!.rootViewController = UIViewController() 36 | 37 | let rootVC = UIApplication.sharedApplication().keyWindow!.rootViewController! 38 | 39 | rootVC.presentViewController(self.gpaViewController, animated: false, completion: { 40 | self.gpaViewController.gpaViewController.searchBar( 41 | self.gpaViewController.gpaViewController.searchBar, 42 | textDidChange: "Paris" 43 | ) 44 | }) 45 | 46 | self.waitForExpectationsWithTimeout(2.0, handler: nil) 47 | } 48 | 49 | func placesFound(places: [Place]) { 50 | expectation.fulfill() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExampleTests/ReferenceImages_64/GooglePlacesAutocompleteExampleTests.GooglePlacesAutocompleteTests/testGooglePlacesAutocomplete_search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/watsonbox/ios_google_places_autocomplete/3b24a37eca923c5d92253122f26a2cd41f10ec5a/GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExampleTests/ReferenceImages_64/GooglePlacesAutocompleteExampleTests.GooglePlacesAutocompleteTests/testGooglePlacesAutocomplete_search@2x.png -------------------------------------------------------------------------------- /GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExampleTests/ReferenceImages_64/GooglePlacesAutocompleteExampleTests.GooglePlacesAutocompleteTests/testGooglePlacesAutocomplete_view@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/watsonbox/ios_google_places_autocomplete/3b24a37eca923c5d92253122f26a2cd41f10ec5a/GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExampleTests/ReferenceImages_64/GooglePlacesAutocompleteExampleTests.GooglePlacesAutocompleteTests/testGooglePlacesAutocomplete_view@2x.png -------------------------------------------------------------------------------- /GooglePlacesAutocompleteTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | net.watsonbox.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Howard Wilson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | workspace 'GooglePlacesAutocomplete' 2 | 3 | platform :ios, '8.0' 4 | 5 | use_frameworks! 6 | 7 | target 'GooglePlacesAutocomplete' do 8 | xcodeproj 'GooglePlacesAutocomplete.xcodeproj' 9 | end 10 | 11 | target 'GooglePlacesAutocompleteTests' do 12 | xcodeproj 'GooglePlacesAutocomplete.xcodeproj' 13 | 14 | end 15 | 16 | target 'GooglePlacesAutocompleteExample' do 17 | xcodeproj 'GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample.xcodeproj' 18 | 19 | end 20 | 21 | target 'GooglePlacesAutocompleteExampleTests' do 22 | xcodeproj 'GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample.xcodeproj' 23 | 24 | pod 'OHHTTPStubs', '~> 4.3' 25 | pod 'FBSnapshotTestCase', git: "git@github.com:facebook/ios-snapshot-test-case.git" 26 | end 27 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FBSnapshotTestCase (2.0.4): 3 | - FBSnapshotTestCase/SwiftSupport (= 2.0.4) 4 | - FBSnapshotTestCase/Core (2.0.4) 5 | - FBSnapshotTestCase/SwiftSupport (2.0.4): 6 | - FBSnapshotTestCase/Core 7 | - OHHTTPStubs (4.3.0): 8 | - OHHTTPStubs/Default (= 4.3.0) 9 | - OHHTTPStubs/Core (4.3.0) 10 | - OHHTTPStubs/Default (4.3.0): 11 | - OHHTTPStubs/Core 12 | - OHHTTPStubs/JSON 13 | - OHHTTPStubs/NSURLSession 14 | - OHHTTPStubs/OHPathHelpers 15 | - OHHTTPStubs/JSON (4.3.0): 16 | - OHHTTPStubs/Core 17 | - OHHTTPStubs/NSURLSession (4.3.0): 18 | - OHHTTPStubs/Core 19 | - OHHTTPStubs/OHPathHelpers (4.3.0) 20 | 21 | DEPENDENCIES: 22 | - FBSnapshotTestCase (from `git@github.com:facebook/ios-snapshot-test-case.git`) 23 | - OHHTTPStubs (~> 4.3) 24 | 25 | EXTERNAL SOURCES: 26 | FBSnapshotTestCase: 27 | :git: git@github.com:facebook/ios-snapshot-test-case.git 28 | 29 | CHECKOUT OPTIONS: 30 | FBSnapshotTestCase: 31 | :commit: cf04b3036d08233654184eb533938336c57f39af 32 | :git: git@github.com:facebook/ios-snapshot-test-case.git 33 | 34 | SPEC CHECKSUMS: 35 | FBSnapshotTestCase: 6c9fe22920e7bc8a98b6b3703ba24cd5ba0978ce 36 | OHHTTPStubs: 0aec5755528693a165bd616cb79f69387de306a8 37 | 38 | COCOAPODS: 0.39.0 39 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Gabriel Handford on 3/1/09. 3 | // Copyright 2009-2013. All rights reserved. 4 | // Created by John Boiles on 10/20/11. 5 | // Copyright (c) 2011. All rights reserved 6 | // Modified by Felix Schulze on 2/11/13. 7 | // Copyright 2013. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person 10 | // obtaining a copy of this software and associated documentation 11 | // files (the "Software"), to deal in the Software without 12 | // restriction, including without limitation the rights to use, 13 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the 15 | // Software is furnished to do so, subject to the following 16 | // conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be 19 | // included in all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 23 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 26 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | // OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #import 32 | 33 | @interface UIImage (Compare) 34 | 35 | - (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Gabriel Handford on 3/1/09. 3 | // Copyright 2009-2013. All rights reserved. 4 | // Created by John Boiles on 10/20/11. 5 | // Copyright (c) 2011. All rights reserved 6 | // Modified by Felix Schulze on 2/11/13. 7 | // Copyright 2013. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person 10 | // obtaining a copy of this software and associated documentation 11 | // files (the "Software"), to deal in the Software without 12 | // restriction, including without limitation the rights to use, 13 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the 15 | // Software is furnished to do so, subject to the following 16 | // conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be 19 | // included in all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 23 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 26 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | // OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #import 32 | 33 | // This makes debugging much more fun 34 | typedef union { 35 | uint32_t raw; 36 | unsigned char bytes[4]; 37 | struct { 38 | char red; 39 | char green; 40 | char blue; 41 | char alpha; 42 | } __attribute__ ((packed)) pixels; 43 | } FBComparePixel; 44 | 45 | @implementation UIImage (Compare) 46 | 47 | - (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance 48 | { 49 | NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size."); 50 | 51 | CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage)); 52 | CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage)); 53 | 54 | // The images have the equal size, so we could use the smallest amount of bytes because of byte padding 55 | size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage)); 56 | size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow; 57 | void *referenceImagePixels = calloc(1, referenceImageSizeBytes); 58 | void *imagePixels = calloc(1, referenceImageSizeBytes); 59 | 60 | if (!referenceImagePixels || !imagePixels) { 61 | free(referenceImagePixels); 62 | free(imagePixels); 63 | return NO; 64 | } 65 | 66 | CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels, 67 | referenceImageSize.width, 68 | referenceImageSize.height, 69 | CGImageGetBitsPerComponent(self.CGImage), 70 | minBytesPerRow, 71 | CGImageGetColorSpace(self.CGImage), 72 | (CGBitmapInfo)kCGImageAlphaPremultipliedLast 73 | ); 74 | CGContextRef imageContext = CGBitmapContextCreate(imagePixels, 75 | imageSize.width, 76 | imageSize.height, 77 | CGImageGetBitsPerComponent(image.CGImage), 78 | minBytesPerRow, 79 | CGImageGetColorSpace(image.CGImage), 80 | (CGBitmapInfo)kCGImageAlphaPremultipliedLast 81 | ); 82 | 83 | if (!referenceImageContext || !imageContext) { 84 | CGContextRelease(referenceImageContext); 85 | CGContextRelease(imageContext); 86 | free(referenceImagePixels); 87 | free(imagePixels); 88 | return NO; 89 | } 90 | 91 | CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage); 92 | CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage); 93 | 94 | CGContextRelease(referenceImageContext); 95 | CGContextRelease(imageContext); 96 | 97 | BOOL imageEqual = YES; 98 | 99 | // Do a fast compare if we can 100 | if (tolerance == 0) { 101 | imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0); 102 | } else { 103 | // Go through each pixel in turn and see if it is different 104 | const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height; 105 | 106 | FBComparePixel *p1 = referenceImagePixels; 107 | FBComparePixel *p2 = imagePixels; 108 | 109 | NSInteger numDiffPixels = 0; 110 | for (int n = 0; n < pixelCount; ++n) { 111 | // If this pixel is different, increment the pixel diff count and see 112 | // if we have hit our limit. 113 | if (p1->raw != p2->raw) { 114 | numDiffPixels ++; 115 | 116 | CGFloat percent = (CGFloat)numDiffPixels / pixelCount; 117 | if (percent > tolerance) { 118 | imageEqual = NO; 119 | break; 120 | } 121 | } 122 | 123 | p1++; 124 | p2++; 125 | } 126 | } 127 | 128 | free(referenceImagePixels); 129 | free(imagePixels); 130 | 131 | return imageEqual; 132 | } 133 | 134 | @end 135 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Gabriel Handford on 3/1/09. 3 | // Copyright 2009-2013. All rights reserved. 4 | // Created by John Boiles on 10/20/11. 5 | // Copyright (c) 2011. All rights reserved 6 | // Modified by Felix Schulze on 2/11/13. 7 | // Copyright 2013. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person 10 | // obtaining a copy of this software and associated documentation 11 | // files (the "Software"), to deal in the Software without 12 | // restriction, including without limitation the rights to use, 13 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the 15 | // Software is furnished to do so, subject to the following 16 | // conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be 19 | // included in all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 23 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 26 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | // OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #import 32 | 33 | @interface UIImage (Diff) 34 | 35 | - (UIImage *)fb_diffWithImage:(UIImage *)image; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Gabriel Handford on 3/1/09. 3 | // Copyright 2009-2013. All rights reserved. 4 | // Created by John Boiles on 10/20/11. 5 | // Copyright (c) 2011. All rights reserved 6 | // Modified by Felix Schulze on 2/11/13. 7 | // Copyright 2013. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person 10 | // obtaining a copy of this software and associated documentation 11 | // files (the "Software"), to deal in the Software without 12 | // restriction, including without limitation the rights to use, 13 | // copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the 15 | // Software is furnished to do so, subject to the following 16 | // conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be 19 | // included in all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 23 | // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 25 | // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 26 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | // OTHER DEALINGS IN THE SOFTWARE. 29 | // 30 | 31 | #import 32 | 33 | @implementation UIImage (Diff) 34 | 35 | - (UIImage *)fb_diffWithImage:(UIImage *)image 36 | { 37 | if (!image) { 38 | return nil; 39 | } 40 | CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height)); 41 | UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0); 42 | CGContextRef context = UIGraphicsGetCurrentContext(); 43 | [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; 44 | CGContextSetAlpha(context, 0.5); 45 | CGContextBeginTransparencyLayer(context, NULL); 46 | [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)]; 47 | CGContextSetBlendMode(context, kCGBlendModeDifference); 48 | CGContextSetFillColorWithColor(context,[UIColor whiteColor].CGColor); 49 | CGContextFillRect(context, CGRectMake(0, 0, self.size.width, self.size.height)); 50 | CGContextEndTransparencyLayer(context); 51 | UIImage *returnImage = UIGraphicsGetImageFromCurrentImageContext(); 52 | UIGraphicsEndImageContext(); 53 | return returnImage; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | #import 12 | 13 | @interface UIImage (Snapshot) 14 | 15 | /// Uses renderInContext: to get a snapshot of the layer. 16 | + (UIImage *)fb_imageForLayer:(CALayer *)layer; 17 | 18 | /// Uses renderInContext: to get a snapshot of the view layer. 19 | + (UIImage *)fb_imageForViewLayer:(UIView *)view; 20 | 21 | /// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed. 22 | + (UIImage *)fb_imageForView:(UIView *)view; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | #import 12 | 13 | @implementation UIImage (Snapshot) 14 | 15 | + (UIImage *)fb_imageForLayer:(CALayer *)layer 16 | { 17 | CGRect bounds = layer.bounds; 18 | NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer); 19 | NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer); 20 | 21 | UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); 22 | CGContextRef context = UIGraphicsGetCurrentContext(); 23 | NSAssert1(context, @"Could not generate context for layer %@", layer); 24 | CGContextSaveGState(context); 25 | [layer layoutIfNeeded]; 26 | [layer renderInContext:context]; 27 | CGContextRestoreGState(context); 28 | 29 | UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); 30 | UIGraphicsEndImageContext(); 31 | return snapshot; 32 | } 33 | 34 | + (UIImage *)fb_imageForViewLayer:(UIView *)view 35 | { 36 | [view layoutIfNeeded]; 37 | return [self fb_imageForLayer:view.layer]; 38 | } 39 | 40 | + (UIImage *)fb_imageForView:(UIView *)view 41 | { 42 | CGRect bounds = view.bounds; 43 | NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view); 44 | NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view); 45 | 46 | UIWindow *window = view.window; 47 | if (window == nil) { 48 | window = [[UIWindow alloc] initWithFrame:bounds]; 49 | [window addSubview:view]; 50 | [window makeKeyAndVisible]; 51 | } 52 | 53 | UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); 54 | [view layoutIfNeeded]; 55 | [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES]; 56 | 57 | UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); 58 | UIGraphicsEndImageContext(); 59 | return snapshot; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | #import 12 | 13 | #import 14 | 15 | #import 16 | 17 | #import 18 | 19 | /* 20 | There are three ways of setting reference image directories. 21 | 22 | 1. Set the preprocessor macro FB_REFERENCE_IMAGE_DIR to a double quoted 23 | c-string with the path. 24 | 2. Set an environment variable named FB_REFERENCE_IMAGE_DIR with the path. This 25 | takes precedence over the preprocessor macro to allow for run-time override. 26 | 3. Keep everything unset, which will cause the reference images to be looked up 27 | inside the bundle holding the current test, in the 28 | Resources/ReferenceImages_* directories. 29 | */ 30 | #ifndef FB_REFERENCE_IMAGE_DIR 31 | #define FB_REFERENCE_IMAGE_DIR "" 32 | #endif 33 | 34 | /** 35 | Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though. 36 | @param view The view to snapshot 37 | @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method. 38 | @param suffixes An NSOrderedSet of strings for the different suffixes 39 | @param tolerance The percentage of pixels that can differ and still count as an 'identical' view 40 | */ 41 | #define FBSnapshotVerifyViewWithOptions(view__, identifier__, suffixes__, tolerance__) \ 42 | FBSnapshotVerifyViewOrLayerWithOptions(View, view__, identifier__, suffixes__, tolerance__) 43 | 44 | #define FBSnapshotVerifyView(view__, identifier__) \ 45 | FBSnapshotVerifyViewWithOptions(view__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0) 46 | 47 | 48 | /** 49 | Similar to our much-loved XCTAssert() macros. Use this to perform your test. No need to write an explanation, though. 50 | @param layer The layer to snapshot 51 | @param identifier An optional identifier, used is there are multiple snapshot tests in a given -test method. 52 | @param suffixes An NSOrderedSet of strings for the different suffixes 53 | @param tolerance The percentage of pixels that can differ and still count as an 'identical' layer 54 | */ 55 | #define FBSnapshotVerifyLayerWithOptions(layer__, identifier__, suffixes__, tolerance__) \ 56 | FBSnapshotVerifyViewOrLayerWithOptions(Layer, layer__, identifier__, suffixes__, tolerance__) 57 | 58 | #define FBSnapshotVerifyLayer(layer__, identifier__) \ 59 | FBSnapshotVerifyLayerWithOptions(layer__, identifier__, FBSnapshotTestCaseDefaultSuffixes(), 0) 60 | 61 | 62 | #define FBSnapshotVerifyViewOrLayerWithOptions(what__, viewOrLayer__, identifier__, suffixes__, tolerance__) \ 63 | { \ 64 | NSString *referenceImageDirectory = [self getReferenceImageDirectoryWithDefault:(@ FB_REFERENCE_IMAGE_DIR)]; \ 65 | XCTAssertNotNil(referenceImageDirectory, @"Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.");\ 66 | XCTAssertTrue((suffixes__.count > 0), @"Suffixes set cannot be empty %@", suffixes__); \ 67 | NSError *error__ = nil; \ 68 | BOOL comparisonSuccess__; \ 69 | for (NSString *suffix__ in suffixes__) { \ 70 | NSString *referenceImagesDirectory__ = [NSString stringWithFormat:@"%@%@", referenceImageDirectory, suffix__]; \ 71 | comparisonSuccess__ = [self compareSnapshotOf ## what__ :(viewOrLayer__) referenceImagesDirectory:referenceImagesDirectory__ identifier:(identifier__) tolerance:(tolerance__) error:&error__]; \ 72 | if (comparisonSuccess__ || self.recordMode) break; \ 73 | } \ 74 | XCTAssertTrue(comparisonSuccess__, @"Snapshot comparison failed: %@", error__); \ 75 | XCTAssertFalse(self.recordMode, @"Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!"); \ 76 | } 77 | 78 | 79 | /** 80 | The base class of view snapshotting tests. If you have small UI component, it's often easier to configure it in a test 81 | and compare an image of the view to a reference image that write lots of complex layout-code tests. 82 | 83 | In order to flip the tests in your subclass to record the reference images set @c recordMode to @c YES. 84 | 85 | @attention When recording, the reference image directory should be explicitly 86 | set, otherwise the images may be written to somewhere inside the 87 | simulator directory. 88 | 89 | For example: 90 | @code 91 | - (void)setUp 92 | { 93 | [super setUp]; 94 | self.recordMode = YES; 95 | } 96 | @endcode 97 | */ 98 | @interface FBSnapshotTestCase : XCTestCase 99 | 100 | /** 101 | When YES, the test macros will save reference images, rather than performing an actual test. 102 | */ 103 | @property (readwrite, nonatomic, assign) BOOL recordMode; 104 | 105 | /** 106 | When @c YES appends the name of the device model and OS to the snapshot file name. 107 | The default value is @c NO. 108 | */ 109 | @property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic; 110 | 111 | /** 112 | When YES, renders a snapshot of the complete view hierarchy as visible onscreen. 113 | There are several things that do not work if renderInContext: is used. 114 | - UIVisualEffect #70 115 | - UIAppearance #91 116 | - Size Classes #92 117 | 118 | @attention If the view does't belong to a UIWindow, it will create one and add the view as a subview. 119 | */ 120 | @property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect; 121 | 122 | - (void)setUp NS_REQUIRES_SUPER; 123 | - (void)tearDown NS_REQUIRES_SUPER; 124 | 125 | /** 126 | Performs the comparison or records a snapshot of the layer if recordMode is YES. 127 | @param layer The Layer to snapshot 128 | @param referenceImagesDirectory The directory in which reference images are stored. 129 | @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method. 130 | @param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care 131 | @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). 132 | @returns YES if the comparison (or saving of the reference image) succeeded. 133 | */ 134 | - (BOOL)compareSnapshotOfLayer:(CALayer *)layer 135 | referenceImagesDirectory:(NSString *)referenceImagesDirectory 136 | identifier:(NSString *)identifier 137 | tolerance:(CGFloat)tolerance 138 | error:(NSError **)errorPtr; 139 | 140 | /** 141 | Performs the comparison or records a snapshot of the view if recordMode is YES. 142 | @param view The view to snapshot 143 | @param referenceImagesDirectory The directory in which reference images are stored. 144 | @param identifier An optional identifier, used if there are multiple snapshot tests in a given -test method. 145 | @param tolerance The percentage difference to still count as identical - 0 mean pixel perfect, 1 means I don't care 146 | @param errorPtr An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). 147 | @returns YES if the comparison (or saving of the reference image) succeeded. 148 | */ 149 | - (BOOL)compareSnapshotOfView:(UIView *)view 150 | referenceImagesDirectory:(NSString *)referenceImagesDirectory 151 | identifier:(NSString *)identifier 152 | tolerance:(CGFloat)tolerance 153 | error:(NSError **)errorPtr; 154 | 155 | /** 156 | Returns the reference image directory. 157 | 158 | Helper function used to implement the assert macros. 159 | 160 | @param dir directory to use if environment variable not specified. Ignored if null or empty. 161 | */ 162 | - (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir; 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | #import 12 | #import 13 | 14 | @implementation FBSnapshotTestCase 15 | { 16 | FBSnapshotTestController *_snapshotController; 17 | } 18 | 19 | #pragma mark - Overrides 20 | 21 | - (void)setUp 22 | { 23 | [super setUp]; 24 | _snapshotController = [[FBSnapshotTestController alloc] initWithTestName:NSStringFromClass([self class])]; 25 | } 26 | 27 | - (void)tearDown 28 | { 29 | _snapshotController = nil; 30 | [super tearDown]; 31 | } 32 | 33 | - (BOOL)recordMode 34 | { 35 | return _snapshotController.recordMode; 36 | } 37 | 38 | - (void)setRecordMode:(BOOL)recordMode 39 | { 40 | NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); 41 | _snapshotController.recordMode = recordMode; 42 | } 43 | 44 | - (BOOL)isDeviceAgnostic 45 | { 46 | return _snapshotController.deviceAgnostic; 47 | } 48 | 49 | - (void)setDeviceAgnostic:(BOOL)deviceAgnostic 50 | { 51 | NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); 52 | _snapshotController.deviceAgnostic = deviceAgnostic; 53 | } 54 | 55 | - (BOOL)usesDrawViewHierarchyInRect 56 | { 57 | return _snapshotController.usesDrawViewHierarchyInRect; 58 | } 59 | 60 | - (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect 61 | { 62 | NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); 63 | _snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect; 64 | } 65 | 66 | #pragma mark - Public API 67 | 68 | - (BOOL)compareSnapshotOfLayer:(CALayer *)layer 69 | referenceImagesDirectory:(NSString *)referenceImagesDirectory 70 | identifier:(NSString *)identifier 71 | tolerance:(CGFloat)tolerance 72 | error:(NSError **)errorPtr 73 | { 74 | return [self _compareSnapshotOfViewOrLayer:layer 75 | referenceImagesDirectory:referenceImagesDirectory 76 | identifier:identifier 77 | tolerance:tolerance 78 | error:errorPtr]; 79 | } 80 | 81 | - (BOOL)compareSnapshotOfView:(UIView *)view 82 | referenceImagesDirectory:(NSString *)referenceImagesDirectory 83 | identifier:(NSString *)identifier 84 | tolerance:(CGFloat)tolerance 85 | error:(NSError **)errorPtr 86 | { 87 | return [self _compareSnapshotOfViewOrLayer:view 88 | referenceImagesDirectory:referenceImagesDirectory 89 | identifier:identifier 90 | tolerance:tolerance 91 | error:errorPtr]; 92 | } 93 | 94 | - (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir 95 | { 96 | NSString *envReferenceImageDirectory = [NSProcessInfo processInfo].environment[@"FB_REFERENCE_IMAGE_DIR"]; 97 | if (envReferenceImageDirectory) { 98 | return envReferenceImageDirectory; 99 | } 100 | if (dir && dir.length > 0) { 101 | return dir; 102 | } 103 | return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"]; 104 | } 105 | 106 | 107 | #pragma mark - Private API 108 | 109 | - (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer 110 | referenceImagesDirectory:(NSString *)referenceImagesDirectory 111 | identifier:(NSString *)identifier 112 | tolerance:(CGFloat)tolerance 113 | error:(NSError **)errorPtr 114 | { 115 | _snapshotController.referenceImagesDirectory = referenceImagesDirectory; 116 | return [_snapshotController compareSnapshotOfViewOrLayer:viewOrLayer 117 | selector:self.invocation.selector 118 | identifier:identifier 119 | tolerance:tolerance 120 | error:errorPtr]; 121 | } 122 | 123 | @end 124 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | #import 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /** 18 | Returns a Boolean value that indicates whether the snapshot test is running in 64Bit. 19 | This method is a convenience for creating the suffixes set based on the architecture 20 | that the test is running. 21 | 22 | @returns @c YES if the test is running in 64bit, otherwise @c NO. 23 | */ 24 | BOOL FBSnapshotTestCaseIs64Bit(void); 25 | 26 | /** 27 | Returns a default set of strings that is used to append a suffix based on the architectures. 28 | @warning Do not modify this function, you can create your own and use it with @c FBSnapshotVerifyViewWithOptions() 29 | 30 | @returns An @c NSOrderedSet object containing strings that are appended to the reference images directory. 31 | */ 32 | NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void); 33 | 34 | /** 35 | Returns a fully «normalized» file name. 36 | Strips punctuation and spaces and replaces them with @c _. Also appends the device model, running OS and screen size to the file name. 37 | 38 | @returns An @c NSString object containing the passed @c fileName with the device model, OS and screen size appended at the end. 39 | */ 40 | NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | #import 12 | #import 13 | 14 | BOOL FBSnapshotTestCaseIs64Bit(void) 15 | { 16 | #if __LP64__ 17 | return YES; 18 | #else 19 | return NO; 20 | #endif 21 | } 22 | 23 | NSOrderedSet *FBSnapshotTestCaseDefaultSuffixes(void) 24 | { 25 | NSMutableOrderedSet *suffixesSet = [[NSMutableOrderedSet alloc] init]; 26 | [suffixesSet addObject:@"_32"]; 27 | [suffixesSet addObject:@"_64"]; 28 | if (FBSnapshotTestCaseIs64Bit()) { 29 | return [suffixesSet reversedOrderedSet]; 30 | } 31 | return [suffixesSet copy]; 32 | } 33 | 34 | NSString *FBDeviceAgnosticNormalizedFileName(NSString *fileName) 35 | { 36 | UIDevice *device = [UIDevice currentDevice]; 37 | CGSize screenSize = [[UIApplication sharedApplication] keyWindow].bounds.size; 38 | NSString *os = device.systemVersion; 39 | 40 | fileName = [NSString stringWithFormat:@"%@_%@%@_%.0fx%.0f", fileName, device.model, os, screenSize.width, screenSize.height]; 41 | 42 | NSMutableCharacterSet *invalidCharacters = [NSMutableCharacterSet new]; 43 | [invalidCharacters formUnionWithCharacterSet:[NSCharacterSet whitespaceCharacterSet]]; 44 | [invalidCharacters formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]]; 45 | NSArray *validComponents = [fileName componentsSeparatedByCharactersInSet:invalidCharacters]; 46 | fileName = [validComponents componentsJoinedByString:@"_"]; 47 | 48 | return fileName; 49 | } -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | #import 12 | #import 13 | 14 | typedef NS_ENUM(NSInteger, FBSnapshotTestControllerErrorCode) { 15 | FBSnapshotTestControllerErrorCodeUnknown, 16 | FBSnapshotTestControllerErrorCodeNeedsRecord, 17 | FBSnapshotTestControllerErrorCodePNGCreationFailed, 18 | FBSnapshotTestControllerErrorCodeImagesDifferentSizes, 19 | FBSnapshotTestControllerErrorCodeImagesDifferent, 20 | }; 21 | /** 22 | Errors returned by the methods of FBSnapshotTestController use this domain. 23 | */ 24 | extern NSString *const FBSnapshotTestControllerErrorDomain; 25 | 26 | /** 27 | Errors returned by the methods of FBSnapshotTestController sometimes contain this key in the `userInfo` dictionary. 28 | */ 29 | extern NSString *const FBReferenceImageFilePathKey; 30 | 31 | /** 32 | Provides the heavy-lifting for FBSnapshotTestCase. It loads and saves images, along with performing the actual pixel- 33 | by-pixel comparison of images. 34 | Instances are initialized with the test class, and directories to read and write to. 35 | */ 36 | @interface FBSnapshotTestController : NSObject 37 | 38 | /** 39 | Record snapshots. 40 | */ 41 | @property (readwrite, nonatomic, assign) BOOL recordMode; 42 | 43 | /** 44 | When @c YES appends the name of the device model and OS to the snapshot file name. 45 | The default value is @c NO. 46 | */ 47 | @property (readwrite, nonatomic, assign, getter=isDeviceAgnostic) BOOL deviceAgnostic; 48 | 49 | /** 50 | Uses drawViewHierarchyInRect:afterScreenUpdates: to draw the image instead of renderInContext: 51 | */ 52 | @property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect; 53 | 54 | /** 55 | The directory in which referfence images are stored. 56 | */ 57 | @property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory; 58 | 59 | /** 60 | @param testClass The subclass of FBSnapshotTestCase that is using this controller. 61 | @returns An instance of FBSnapshotTestController. 62 | */ 63 | - (instancetype)initWithTestClass:(Class)testClass; 64 | 65 | /** 66 | Designated initializer. 67 | @param testName The name of the tests. 68 | @returns An instance of FBSnapshotTestController. 69 | */ 70 | - (instancetype)initWithTestName:(NSString *)testName; 71 | 72 | /** 73 | Performs the comparison of the layer. 74 | @param layer The Layer to snapshot. 75 | @param selector The test method being run. 76 | @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method. 77 | @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). 78 | @returns YES if the comparison (or saving of the reference image) succeeded. 79 | */ 80 | - (BOOL)compareSnapshotOfLayer:(CALayer *)layer 81 | selector:(SEL)selector 82 | identifier:(NSString *)identifier 83 | error:(NSError **)errorPtr; 84 | 85 | /** 86 | Performs the comparison of the view. 87 | @param view The view to snapshot. 88 | @param selector The test method being run. 89 | @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method. 90 | @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). 91 | @returns YES if the comparison (or saving of the reference image) succeeded. 92 | */ 93 | - (BOOL)compareSnapshotOfView:(UIView *)view 94 | selector:(SEL)selector 95 | identifier:(NSString *)identifier 96 | error:(NSError **)errorPtr; 97 | 98 | /** 99 | Performs the comparison of a view or layer. 100 | @param view The view or layer to snapshot. 101 | @param selector The test method being run. 102 | @param identifier An optional identifier, used is there are muliptle snapshot tests in a given -test method. 103 | @param tolerance The percentage of pixels that can differ and still be considered 'identical' 104 | @param error An error to log in an XCTAssert() macro if the method fails (missing reference image, images differ, etc). 105 | @returns YES if the comparison (or saving of the reference image) succeeded. 106 | */ 107 | - (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer 108 | selector:(SEL)selector 109 | identifier:(NSString *)identifier 110 | tolerance:(CGFloat)tolerance 111 | error:(NSError **)errorPtr; 112 | 113 | /** 114 | Loads a reference image. 115 | @param selector The test method being run. 116 | @param identifier The optional identifier, used when multiple images are tested in a single -test method. 117 | @param errorPtr An error, if this methods returns nil, the error will be something useful. 118 | @returns An image. 119 | */ 120 | - (UIImage *)referenceImageForSelector:(SEL)selector 121 | identifier:(NSString *)identifier 122 | error:(NSError **)errorPtr; 123 | 124 | /** 125 | Performs a pixel-by-pixel comparison of the two images with an allowable margin of error. 126 | @param referenceImage The reference (correct) image. 127 | @param image The image to test against the reference. 128 | @param tolerance The percentage of pixels that can differ and still be considered 'identical' 129 | @param errorPtr An error that indicates why the comparison failed if it does. 130 | @returns YES if the comparison succeeded and the images are the same(ish). 131 | */ 132 | - (BOOL)compareReferenceImage:(UIImage *)referenceImage 133 | toImage:(UIImage *)image 134 | tolerance:(CGFloat)tolerance 135 | error:(NSError **)errorPtr; 136 | 137 | /** 138 | Saves the reference image and the test image to `failedOutputDirectory`. 139 | @param referenceImage The reference (correct) image. 140 | @param testImage The image to test against the reference. 141 | @param selector The test method being run. 142 | @param identifier The optional identifier, used when multiple images are tested in a single -test method. 143 | @param errorPtr An error that indicates why the comparison failed if it does. 144 | @returns YES if the save succeeded. 145 | */ 146 | - (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage 147 | testImage:(UIImage *)testImage 148 | selector:(SEL)selector 149 | identifier:(NSString *)identifier 150 | error:(NSError **)errorPtr; 151 | @end 152 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | #import 18 | 19 | NSString *const FBSnapshotTestControllerErrorDomain = @"FBSnapshotTestControllerErrorDomain"; 20 | NSString *const FBReferenceImageFilePathKey = @"FBReferenceImageFilePathKey"; 21 | 22 | typedef NS_ENUM(NSUInteger, FBTestSnapshotFileNameType) { 23 | FBTestSnapshotFileNameTypeReference, 24 | FBTestSnapshotFileNameTypeFailedReference, 25 | FBTestSnapshotFileNameTypeFailedTest, 26 | FBTestSnapshotFileNameTypeFailedTestDiff, 27 | }; 28 | 29 | @implementation FBSnapshotTestController 30 | { 31 | NSString *_testName; 32 | NSFileManager *_fileManager; 33 | } 34 | 35 | #pragma mark - Initializers 36 | 37 | - (instancetype)initWithTestClass:(Class)testClass; 38 | { 39 | return [self initWithTestName:NSStringFromClass(testClass)]; 40 | } 41 | 42 | - (instancetype)initWithTestName:(NSString *)testName 43 | { 44 | if (self = [super init]) { 45 | _testName = [testName copy]; 46 | _deviceAgnostic = NO; 47 | 48 | _fileManager = [[NSFileManager alloc] init]; 49 | } 50 | return self; 51 | } 52 | 53 | #pragma mark - Overrides 54 | 55 | - (NSString *)description 56 | { 57 | return [NSString stringWithFormat:@"%@ %@", [super description], _referenceImagesDirectory]; 58 | } 59 | 60 | #pragma mark - Public API 61 | 62 | - (BOOL)compareSnapshotOfLayer:(CALayer *)layer 63 | selector:(SEL)selector 64 | identifier:(NSString *)identifier 65 | error:(NSError **)errorPtr 66 | { 67 | return [self compareSnapshotOfViewOrLayer:layer 68 | selector:selector 69 | identifier:identifier 70 | tolerance:0 71 | error:errorPtr]; 72 | } 73 | 74 | - (BOOL)compareSnapshotOfView:(UIView *)view 75 | selector:(SEL)selector 76 | identifier:(NSString *)identifier 77 | error:(NSError **)errorPtr 78 | { 79 | return [self compareSnapshotOfViewOrLayer:view 80 | selector:selector 81 | identifier:identifier 82 | tolerance:0 83 | error:errorPtr]; 84 | } 85 | 86 | - (BOOL)compareSnapshotOfViewOrLayer:(id)viewOrLayer 87 | selector:(SEL)selector 88 | identifier:(NSString *)identifier 89 | tolerance:(CGFloat)tolerance 90 | error:(NSError **)errorPtr 91 | { 92 | if (self.recordMode) { 93 | return [self _recordSnapshotOfViewOrLayer:viewOrLayer selector:selector identifier:identifier error:errorPtr]; 94 | } else { 95 | return [self _performPixelComparisonWithViewOrLayer:viewOrLayer selector:selector identifier:identifier tolerance:tolerance error:errorPtr]; 96 | } 97 | } 98 | 99 | - (UIImage *)referenceImageForSelector:(SEL)selector 100 | identifier:(NSString *)identifier 101 | error:(NSError **)errorPtr 102 | { 103 | NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier]; 104 | UIImage *image = [UIImage imageWithContentsOfFile:filePath]; 105 | if (nil == image && NULL != errorPtr) { 106 | BOOL exists = [_fileManager fileExistsAtPath:filePath]; 107 | if (!exists) { 108 | *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain 109 | code:FBSnapshotTestControllerErrorCodeNeedsRecord 110 | userInfo:@{ 111 | FBReferenceImageFilePathKey: filePath, 112 | NSLocalizedDescriptionKey: @"Unable to load reference image.", 113 | NSLocalizedFailureReasonErrorKey: @"Reference image not found. You need to run the test in record mode", 114 | }]; 115 | } else { 116 | *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain 117 | code:FBSnapshotTestControllerErrorCodeUnknown 118 | userInfo:nil]; 119 | } 120 | } 121 | return image; 122 | } 123 | 124 | - (BOOL)compareReferenceImage:(UIImage *)referenceImage 125 | toImage:(UIImage *)image 126 | tolerance:(CGFloat)tolerance 127 | error:(NSError **)errorPtr 128 | { 129 | if (CGSizeEqualToSize(referenceImage.size, image.size)) { 130 | BOOL imagesEqual = [referenceImage fb_compareWithImage:image tolerance:tolerance]; 131 | if (NULL != errorPtr) { 132 | *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain 133 | code:FBSnapshotTestControllerErrorCodeImagesDifferent 134 | userInfo:@{ 135 | NSLocalizedDescriptionKey: @"Images different", 136 | }]; 137 | } 138 | return imagesEqual; 139 | } 140 | if (NULL != errorPtr) { 141 | *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain 142 | code:FBSnapshotTestControllerErrorCodeImagesDifferentSizes 143 | userInfo:@{ 144 | NSLocalizedDescriptionKey: @"Images different sizes", 145 | NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"referenceImage:%@, image:%@", 146 | NSStringFromCGSize(referenceImage.size), 147 | NSStringFromCGSize(image.size)], 148 | }]; 149 | } 150 | return NO; 151 | } 152 | 153 | - (BOOL)saveFailedReferenceImage:(UIImage *)referenceImage 154 | testImage:(UIImage *)testImage 155 | selector:(SEL)selector 156 | identifier:(NSString *)identifier 157 | error:(NSError **)errorPtr 158 | { 159 | NSData *referencePNGData = UIImagePNGRepresentation(referenceImage); 160 | NSData *testPNGData = UIImagePNGRepresentation(testImage); 161 | 162 | NSString *referencePath = [self _failedFilePathForSelector:selector 163 | identifier:identifier 164 | fileNameType:FBTestSnapshotFileNameTypeFailedReference]; 165 | 166 | NSError *creationError = nil; 167 | BOOL didCreateDir = [_fileManager createDirectoryAtPath:[referencePath stringByDeletingLastPathComponent] 168 | withIntermediateDirectories:YES 169 | attributes:nil 170 | error:&creationError]; 171 | if (!didCreateDir) { 172 | if (NULL != errorPtr) { 173 | *errorPtr = creationError; 174 | } 175 | return NO; 176 | } 177 | 178 | if (![referencePNGData writeToFile:referencePath options:NSDataWritingAtomic error:errorPtr]) { 179 | return NO; 180 | } 181 | 182 | NSString *testPath = [self _failedFilePathForSelector:selector 183 | identifier:identifier 184 | fileNameType:FBTestSnapshotFileNameTypeFailedTest]; 185 | 186 | if (![testPNGData writeToFile:testPath options:NSDataWritingAtomic error:errorPtr]) { 187 | return NO; 188 | } 189 | 190 | NSString *diffPath = [self _failedFilePathForSelector:selector 191 | identifier:identifier 192 | fileNameType:FBTestSnapshotFileNameTypeFailedTestDiff]; 193 | 194 | UIImage *diffImage = [referenceImage fb_diffWithImage:testImage]; 195 | NSData *diffImageData = UIImagePNGRepresentation(diffImage); 196 | 197 | if (![diffImageData writeToFile:diffPath options:NSDataWritingAtomic error:errorPtr]) { 198 | return NO; 199 | } 200 | 201 | NSLog(@"If you have Kaleidoscope installed you can run this command to see an image diff:\n" 202 | @"ksdiff \"%@\" \"%@\"", referencePath, testPath); 203 | 204 | return YES; 205 | } 206 | 207 | #pragma mark - Private API 208 | 209 | - (NSString *)_fileNameForSelector:(SEL)selector 210 | identifier:(NSString *)identifier 211 | fileNameType:(FBTestSnapshotFileNameType)fileNameType 212 | { 213 | NSString *fileName = nil; 214 | switch (fileNameType) { 215 | case FBTestSnapshotFileNameTypeFailedReference: 216 | fileName = @"reference_"; 217 | break; 218 | case FBTestSnapshotFileNameTypeFailedTest: 219 | fileName = @"failed_"; 220 | break; 221 | case FBTestSnapshotFileNameTypeFailedTestDiff: 222 | fileName = @"diff_"; 223 | break; 224 | default: 225 | fileName = @""; 226 | break; 227 | } 228 | fileName = [fileName stringByAppendingString:NSStringFromSelector(selector)]; 229 | if (0 < identifier.length) { 230 | fileName = [fileName stringByAppendingFormat:@"_%@", identifier]; 231 | } 232 | 233 | if (self.isDeviceAgnostic) { 234 | fileName = FBDeviceAgnosticNormalizedFileName(fileName); 235 | } 236 | 237 | if ([[UIScreen mainScreen] scale] > 1) { 238 | fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]]; 239 | } 240 | fileName = [fileName stringByAppendingPathExtension:@"png"]; 241 | return fileName; 242 | } 243 | 244 | - (NSString *)_referenceFilePathForSelector:(SEL)selector 245 | identifier:(NSString *)identifier 246 | { 247 | NSString *fileName = [self _fileNameForSelector:selector 248 | identifier:identifier 249 | fileNameType:FBTestSnapshotFileNameTypeReference]; 250 | NSString *filePath = [_referenceImagesDirectory stringByAppendingPathComponent:_testName]; 251 | filePath = [filePath stringByAppendingPathComponent:fileName]; 252 | return filePath; 253 | } 254 | 255 | - (NSString *)_failedFilePathForSelector:(SEL)selector 256 | identifier:(NSString *)identifier 257 | fileNameType:(FBTestSnapshotFileNameType)fileNameType 258 | { 259 | NSString *fileName = [self _fileNameForSelector:selector 260 | identifier:identifier 261 | fileNameType:fileNameType]; 262 | NSString *folderPath = NSTemporaryDirectory(); 263 | if (getenv("IMAGE_DIFF_DIR")) { 264 | folderPath = @(getenv("IMAGE_DIFF_DIR")); 265 | } 266 | NSString *filePath = [folderPath stringByAppendingPathComponent:_testName]; 267 | filePath = [filePath stringByAppendingPathComponent:fileName]; 268 | return filePath; 269 | } 270 | 271 | - (BOOL)_performPixelComparisonWithViewOrLayer:(id)viewOrLayer 272 | selector:(SEL)selector 273 | identifier:(NSString *)identifier 274 | tolerance:(CGFloat)tolerance 275 | error:(NSError **)errorPtr 276 | { 277 | UIImage *referenceImage = [self referenceImageForSelector:selector identifier:identifier error:errorPtr]; 278 | if (nil != referenceImage) { 279 | UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer]; 280 | BOOL imagesSame = [self compareReferenceImage:referenceImage toImage:snapshot tolerance:tolerance error:errorPtr]; 281 | if (!imagesSame) { 282 | [self saveFailedReferenceImage:referenceImage 283 | testImage:snapshot 284 | selector:selector 285 | identifier:identifier 286 | error:errorPtr]; 287 | } 288 | return imagesSame; 289 | } 290 | return NO; 291 | } 292 | 293 | - (BOOL)_recordSnapshotOfViewOrLayer:(id)viewOrLayer 294 | selector:(SEL)selector 295 | identifier:(NSString *)identifier 296 | error:(NSError **)errorPtr 297 | { 298 | UIImage *snapshot = [self _imageForViewOrLayer:viewOrLayer]; 299 | return [self _saveReferenceImage:snapshot selector:selector identifier:identifier error:errorPtr]; 300 | } 301 | 302 | - (BOOL)_saveReferenceImage:(UIImage *)image 303 | selector:(SEL)selector 304 | identifier:(NSString *)identifier 305 | error:(NSError **)errorPtr 306 | { 307 | BOOL didWrite = NO; 308 | if (nil != image) { 309 | NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier]; 310 | NSData *pngData = UIImagePNGRepresentation(image); 311 | if (nil != pngData) { 312 | NSError *creationError = nil; 313 | BOOL didCreateDir = [_fileManager createDirectoryAtPath:[filePath stringByDeletingLastPathComponent] 314 | withIntermediateDirectories:YES 315 | attributes:nil 316 | error:&creationError]; 317 | if (!didCreateDir) { 318 | if (NULL != errorPtr) { 319 | *errorPtr = creationError; 320 | } 321 | return NO; 322 | } 323 | didWrite = [pngData writeToFile:filePath options:NSDataWritingAtomic error:errorPtr]; 324 | if (didWrite) { 325 | NSLog(@"Reference image save at: %@", filePath); 326 | } 327 | } else { 328 | if (nil != errorPtr) { 329 | *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain 330 | code:FBSnapshotTestControllerErrorCodePNGCreationFailed 331 | userInfo:@{ 332 | FBReferenceImageFilePathKey: filePath, 333 | }]; 334 | } 335 | } 336 | } 337 | return didWrite; 338 | } 339 | 340 | - (UIImage *)_imageForViewOrLayer:(id)viewOrLayer 341 | { 342 | if ([viewOrLayer isKindOfClass:[UIView class]]) { 343 | if (_usesDrawViewHierarchyInRect) { 344 | return [UIImage fb_imageForView:viewOrLayer]; 345 | } else { 346 | return [UIImage fb_imageForViewLayer:viewOrLayer]; 347 | } 348 | } else if ([viewOrLayer isKindOfClass:[CALayer class]]) { 349 | return [UIImage fb_imageForLayer:viewOrLayer]; 350 | } else { 351 | [NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer]; 352 | } 353 | return nil; 354 | } 355 | 356 | @end 357 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/FBSnapshotTestCase/SwiftSupport.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | */ 10 | 11 | public extension FBSnapshotTestCase { 12 | public func FBSnapshotVerifyView(view: UIView, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) { 13 | FBSnapshotVerifyViewOrLayer(view, identifier: identifier, suffixes: suffixes) 14 | } 15 | 16 | public func FBSnapshotVerifyLayer(layer: CALayer, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) { 17 | FBSnapshotVerifyViewOrLayer(layer, identifier: identifier, suffixes: suffixes) 18 | } 19 | 20 | private func FBSnapshotVerifyViewOrLayer(viewOrLayer: AnyObject, identifier: String = "", suffixes: NSOrderedSet = FBSnapshotTestCaseDefaultSuffixes(), file: String = __FILE__, line: UInt = __LINE__) { 21 | let envReferenceImageDirectory = self.getReferenceImageDirectoryWithDefault(FB_REFERENCE_IMAGE_DIR) 22 | var error: NSError? 23 | var comparisonSuccess = false 24 | 25 | if let envReferenceImageDirectory = envReferenceImageDirectory { 26 | for suffix in suffixes { 27 | let referenceImagesDirectory = "\(envReferenceImageDirectory)\(suffix)" 28 | if viewOrLayer.isKindOfClass(UIView) { 29 | do { 30 | try compareSnapshotOfView(viewOrLayer as! UIView, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0) 31 | comparisonSuccess = true 32 | } catch let error1 as NSError { 33 | error = error1 34 | comparisonSuccess = false 35 | } 36 | } else if viewOrLayer.isKindOfClass(CALayer) { 37 | do { 38 | try compareSnapshotOfLayer(viewOrLayer as! CALayer, referenceImagesDirectory: referenceImagesDirectory, identifier: identifier, tolerance: 0) 39 | comparisonSuccess = true 40 | } catch let error1 as NSError { 41 | error = error1 42 | comparisonSuccess = false 43 | } 44 | } else { 45 | assertionFailure("Only UIView and CALayer classes can be snapshotted") 46 | } 47 | 48 | assert(recordMode == false, message: "Test ran in record mode. Reference image is now saved. Disable record mode to perform an actual snapshot comparison!", file: file, line: line) 49 | 50 | if comparisonSuccess || recordMode { 51 | break 52 | } 53 | 54 | assert(comparisonSuccess, message: "Snapshot comparison failed: \(error)", file: file, line: line) 55 | } 56 | } else { 57 | XCTFail("Missing value for referenceImagesDirectory - Set FB_REFERENCE_IMAGE_DIR as Environment variable in your scheme.") 58 | } 59 | } 60 | 61 | func assert(assertion: Bool, message: String, file: String, line: UInt) { 62 | if !assertion { 63 | XCTFail(message, file: file, line: line) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For the FBSnapshotTestCase software 4 | 5 | Copyright (c) 2013, Facebook, Inc. 6 | All rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | * Neither the name Facebook nor the names of its contributors may be used to 17 | endorse or promote products derived from this software without specific 18 | prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Pods/FBSnapshotTestCase/README.md: -------------------------------------------------------------------------------- 1 | FBSnapshotTestCase 2 | ====================== 3 | 4 | [![Build Status](https://travis-ci.org/facebook/ios-snapshot-test-case.svg)](https://travis-ci.org/facebook/ios-snapshot-test-case) [![Cocoa Pod Version](https://cocoapod-badges.herokuapp.com/v/FBSnapshotTestCase/badge.svg)](http://cocoadocs.org/docsets/FBSnapshotTestCase/) 5 | 6 | What it does 7 | ------------ 8 | 9 | A "snapshot test case" takes a configured `UIView` or `CALayer` and uses the 10 | `renderInContext:` method to get an image snapshot of its contents. It 11 | compares this snapshot to a "reference image" stored in your source code 12 | repository and fails the test if the two images don't match. 13 | 14 | Why? 15 | ---- 16 | 17 | At Facebook we write a lot of UI code. As you might imagine, each type of 18 | feed story is rendered using a subclass of `UIView`. There are a lot of edge 19 | cases that we want to handle correctly: 20 | 21 | - What if there is more text than can fit in the space available? 22 | - What if an image doesn't match the size of an image view? 23 | - What should the highlighted state look like? 24 | 25 | It's straightforward to test logic code, but less obvious how you should test 26 | views. You can do a lot of rectangle asserts, but these are hard to understand 27 | or visualize. Looking at an image diff shows you exactly what changed and how 28 | it will look to users. 29 | 30 | We developed `FBSnapshotTestCase` to make snapshot tests easy. 31 | 32 | Installation with CocoaPods 33 | --------------------------- 34 | 35 | 1. Add the following lines to your Podfile: 36 | 37 | ``` 38 | target "Tests" do 39 | pod 'FBSnapshotTestCase' 40 | end 41 | ``` 42 | 43 | If you support iOS 7 use `FBSnapshotTestCase/Core` instead, which doesn't contain Swift support. 44 | 45 | Replace "Tests" with the name of your test project. 46 | 47 | 2. There are [three ways](https://github.com/facebook/ios-snapshot-test-case/blob/master/FBSnapshotTestCase/FBSnapshotTestCase.h#L19-L29) of setting reference image directories, the recommended one is to define `FB_REFERENCE_IMAGE_DIR` in your scheme. This should point to the directory where you want reference images to be stored. At Facebook, we normally use this: 48 | 49 | |Name|Value| 50 | |:---|:----| 51 | |`FB_REFERENCE_IMAGE_DIR`|`$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages`| 52 | 53 | 54 | ![](FBSnapshotTestCaseDemo/Scheme_FB_REFERENCE_IMAGE_DIR.png) 55 | 56 | Creating a snapshot test 57 | ------------------------ 58 | 59 | 1. Subclass `FBSnapshotTestCase` instead of `XCTestCase`. 60 | 2. From within your test, use `FBSnapshotVerifyView`. 61 | 3. Run the test once with `self.recordMode = YES;` in the test's `-setUp` 62 | method. (This creates the reference images on disk.) 63 | 4. Remove the line enabling record mode and run the test. 64 | 65 | Features 66 | -------- 67 | 68 | - Automatically names reference images on disk according to test class and 69 | selector. 70 | - Prints a descriptive error message to the console on failure. (Bonus: 71 | failure message includes a one-line command to see an image diff if 72 | you have [Kaleidoscope](http://www.kaleidoscopeapp.com) installed.) 73 | - Supply an optional "identifier" if you want to perform multiple snapshots 74 | in a single test method. 75 | - Support for `CALayer` via `FBSnapshotVerifyLayer`. 76 | - `usesDrawViewHierarchyInRect` to handle cases like `UIVisualEffect`, `UIAppearance` and Size Classes. 77 | - `isDeviceAgnostic` to allow appending the device model (`iPhone`, `iPad`, `iPod Touch`, etc), OS version and screen size to the images (allowing to have multiple tests for the same «snapshot» for different `OS`s and devices). 78 | 79 | Notes 80 | ----- 81 | 82 | Your unit test must be an "application test", not a "logic test." (That is, it 83 | must be run within the Simulator so that it has access to UIKit.) In Xcode 5 84 | and later new projects only offer application tests, but older projects will 85 | have separate targets for the two types. 86 | 87 | Authors 88 | ------- 89 | 90 | `FBSnapshotTestCase` was written at Facebook by 91 | [Jonathan Dann](https://facebook.com/j.p.dann) with significant contributions by 92 | [Todd Krabach](https://facebook.com/toddkrabach). 93 | 94 | License 95 | ------- 96 | 97 | `FBSnapshotTestCase` is BSD-licensed. See `LICENSE`. 98 | -------------------------------------------------------------------------------- /Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestCase.h: -------------------------------------------------------------------------------- 1 | ../../../FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCase.h -------------------------------------------------------------------------------- /Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h: -------------------------------------------------------------------------------- 1 | ../../../FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestCasePlatform.h -------------------------------------------------------------------------------- /Pods/Headers/Private/FBSnapshotTestCase/FBSnapshotTestController.h: -------------------------------------------------------------------------------- 1 | ../../../FBSnapshotTestCase/FBSnapshotTestCase/FBSnapshotTestController.h -------------------------------------------------------------------------------- /Pods/Headers/Private/FBSnapshotTestCase/UIImage+Compare.h: -------------------------------------------------------------------------------- 1 | ../../../FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Compare.h -------------------------------------------------------------------------------- /Pods/Headers/Private/FBSnapshotTestCase/UIImage+Diff.h: -------------------------------------------------------------------------------- 1 | ../../../FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Diff.h -------------------------------------------------------------------------------- /Pods/Headers/Private/FBSnapshotTestCase/UIImage+Snapshot.h: -------------------------------------------------------------------------------- 1 | ../../../FBSnapshotTestCase/FBSnapshotTestCase/Categories/UIImage+Snapshot.h -------------------------------------------------------------------------------- /Pods/Headers/Private/OHHTTPStubs/Compatibility.h: -------------------------------------------------------------------------------- 1 | ../../../OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h -------------------------------------------------------------------------------- /Pods/Headers/Private/OHHTTPStubs/OHHTTPStubs.h: -------------------------------------------------------------------------------- 1 | ../../../OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h -------------------------------------------------------------------------------- /Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse+JSON.h: -------------------------------------------------------------------------------- 1 | ../../../OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h -------------------------------------------------------------------------------- /Pods/Headers/Private/OHHTTPStubs/OHHTTPStubsResponse.h: -------------------------------------------------------------------------------- 1 | ../../../OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h -------------------------------------------------------------------------------- /Pods/Headers/Private/OHHTTPStubs/OHPathHelpers.h: -------------------------------------------------------------------------------- 1 | ../../../OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h -------------------------------------------------------------------------------- /Pods/Local Podspecs/FBSnapshotTestCase.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FBSnapshotTestCase", 3 | "version": "2.0.4", 4 | "summary": "Snapshot view unit tests for iOS", 5 | "description": "A \"snapshot test case\" takes a configured UIView or CALayer\nand uses the renderInContext: method to get an image snapshot\nof its contents. It compares this snapshot to a \"reference image\"\nstored in your source code repository and fails the test if the\ntwo images don't match.", 6 | "homepage": "https://github.com/facebook/ios-snapshot-test-case", 7 | "license": "BSD", 8 | "authors": "Facebook", 9 | "source": { 10 | "git": "https://github.com/facebook/ios-snapshot-test-case.git", 11 | "tag": "2.0.4" 12 | }, 13 | "platforms": { 14 | "ios": "7.0" 15 | }, 16 | "requires_arc": true, 17 | "frameworks": "XCTest", 18 | "pod_target_xcconfig": { 19 | "ENABLE_BITCODE": "NO" 20 | }, 21 | "public_header_files": [ 22 | "FBSnapshotTestCase/FBSnapshotTestCase.h", 23 | "FBSnapshotTestCase/FBSnapshotTestCasePlatform.h" 24 | ], 25 | "private_header_files": [ 26 | "FBSnapshotTestCase/FBSnapshotTestController.h", 27 | "FBSnapshotTestCase/UIImage+Compare.h", 28 | "FBSnapshotTestCase/UIImage+Diff.h" 29 | ], 30 | "default_subspecs": "SwiftSupport", 31 | "subspecs": [ 32 | { 33 | "name": "Core", 34 | "source_files": "FBSnapshotTestCase/**/*.{h,m}" 35 | }, 36 | { 37 | "name": "SwiftSupport", 38 | "dependencies": { 39 | "FBSnapshotTestCase/Core": [ 40 | 41 | ] 42 | }, 43 | "source_files": "FBSnapshotTestCase/**/*.swift" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FBSnapshotTestCase (2.0.4): 3 | - FBSnapshotTestCase/SwiftSupport (= 2.0.4) 4 | - FBSnapshotTestCase/Core (2.0.4) 5 | - FBSnapshotTestCase/SwiftSupport (2.0.4): 6 | - FBSnapshotTestCase/Core 7 | - OHHTTPStubs (4.3.0): 8 | - OHHTTPStubs/Default (= 4.3.0) 9 | - OHHTTPStubs/Core (4.3.0) 10 | - OHHTTPStubs/Default (4.3.0): 11 | - OHHTTPStubs/Core 12 | - OHHTTPStubs/JSON 13 | - OHHTTPStubs/NSURLSession 14 | - OHHTTPStubs/OHPathHelpers 15 | - OHHTTPStubs/JSON (4.3.0): 16 | - OHHTTPStubs/Core 17 | - OHHTTPStubs/NSURLSession (4.3.0): 18 | - OHHTTPStubs/Core 19 | - OHHTTPStubs/OHPathHelpers (4.3.0) 20 | 21 | DEPENDENCIES: 22 | - FBSnapshotTestCase (from `git@github.com:facebook/ios-snapshot-test-case.git`) 23 | - OHHTTPStubs (~> 4.3) 24 | 25 | EXTERNAL SOURCES: 26 | FBSnapshotTestCase: 27 | :git: git@github.com:facebook/ios-snapshot-test-case.git 28 | 29 | CHECKOUT OPTIONS: 30 | FBSnapshotTestCase: 31 | :commit: cf04b3036d08233654184eb533938336c57f39af 32 | :git: git@github.com:facebook/ios-snapshot-test-case.git 33 | 34 | SPEC CHECKSUMS: 35 | FBSnapshotTestCase: 6c9fe22920e7bc8a98b6b3703ba24cd5ba0978ce 36 | OHHTTPStubs: 0aec5755528693a165bd616cb79f69387de306a8 37 | 38 | COCOAPODS: 0.39.0 39 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/LICENSE: -------------------------------------------------------------------------------- 1 | - MIT LICENSE - 2 | 3 | Copyright (c) 2012 Olivier Halligon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/OHHTTPStubs/Sources/Compatibility.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | * 3 | * Copyright (c) 2012 Olivier Halligon 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 | * 23 | ***********************************************************************************/ 24 | 25 | 26 | /* 27 | * This file allows to keep compatibility with older SDKs which didn't have 28 | * the latest features and associated macros yet. 29 | */ 30 | 31 | 32 | #ifndef NS_DESIGNATED_INITIALIZER 33 | #if __has_attribute(objc_designated_initializer) 34 | #define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) 35 | #else 36 | #define NS_DESIGNATED_INITIALIZER 37 | #endif 38 | #endif 39 | 40 | // Allow to use nullability macros and keywords even if not supported yet 41 | #if ! __has_feature(nullability) 42 | #define NS_ASSUME_NONNULL_BEGIN 43 | #define NS_ASSUME_NONNULL_END 44 | #define nullable 45 | #define __nullable 46 | #define __nonnull 47 | #endif 48 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | * 3 | * Copyright (c) 2012 Olivier Halligon 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 | * 23 | ***********************************************************************************/ 24 | 25 | 26 | #import 27 | #import 28 | 29 | NS_ASSUME_NONNULL_BEGIN 30 | 31 | /** 32 | * Adds convenience methods to manipulate JSON objects directly. 33 | * Pass in an `NSDictionary` or `NSArray` to generate a corresponding JSON output. 34 | */ 35 | @interface OHHTTPStubsResponse (JSON) 36 | 37 | /** 38 | * Builds a response given a JSON object for the response body, status code, and headers. 39 | * 40 | * @param jsonObject Object representing the response body. 41 | * Typically a `NSDictionary`; may be any object accepted by `+[NSJSONSerialization dataWithJSONObject:options:error:]` 42 | * @param statusCode The HTTP Status Code to use in the response 43 | * @param httpHeaders The HTTP Headers to return in the response 44 | * If a "Content-Type" header is not included, "Content-Type: application/json" will be added. 45 | * 46 | * @return An `OHHTTPStubsResponse` describing the corresponding response to return by the stub 47 | * 48 | * @note This method typically calls `responseWithData:statusCode:headers:`, passing the serialized JSON 49 | * object as the data parameter and adding the Content-Type header if necessary. 50 | */ 51 | + (instancetype)responseWithJSONObject:(id)jsonObject 52 | statusCode:(int)statusCode 53 | headers:(nullable NSDictionary *)httpHeaders; 54 | 55 | @end 56 | 57 | NS_ASSUME_NONNULL_END 58 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/OHHTTPStubs/Sources/JSON/OHHTTPStubsResponse+JSON.m: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | * 3 | * Copyright (c) 2012 Olivier Halligon 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 | * 23 | ***********************************************************************************/ 24 | 25 | 26 | #import 27 | 28 | @implementation OHHTTPStubsResponse (JSON) 29 | 30 | /*! @name Building a response from JSON objects */ 31 | 32 | + (instancetype)responseWithJSONObject:(id)jsonObject 33 | statusCode:(int)statusCode 34 | headers:(nullable NSDictionary *)httpHeaders 35 | { 36 | if (!httpHeaders[@"Content-Type"]) 37 | { 38 | NSMutableDictionary* mutableHeaders = [NSMutableDictionary dictionaryWithDictionary:httpHeaders]; 39 | mutableHeaders[@"Content-Type"] = @"application/json"; 40 | httpHeaders = [NSDictionary dictionaryWithDictionary:mutableHeaders]; // make immutable again 41 | } 42 | 43 | return [self responseWithData:[NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:nil] 44 | statusCode:statusCode 45 | headers:httpHeaders]; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/OHHTTPStubs/Sources/NSURLSession/OHHTTPStubs+NSURLSessionConfiguration.m: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | * 3 | * Copyright (c) 2012 Olivier Halligon 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 | * 23 | ***********************************************************************************/ 24 | 25 | 26 | #import 27 | 28 | #if defined(__IPHONE_7_0) || defined(__MAC_10_9) 29 | 30 | #import 31 | #import 32 | 33 | 34 | ////////////////////////////////////////////////////////////////////////////////////////////////// 35 | 36 | /** 37 | * This helper is used to swizzle NSURLSessionConfiguration constructor methods 38 | * defaultSessionConfiguration and ephemeralSessionConfiguration to insert the private 39 | * OHHTTPStubsProtocol into their protocolClasses array so that OHHTTPStubs is automagically 40 | * supported when you create a new NSURLSession based on one of there configurations. 41 | */ 42 | 43 | typedef NSURLSessionConfiguration*(*SessionConfigConstructor)(id,SEL); 44 | static SessionConfigConstructor orig_defaultSessionConfiguration; 45 | static SessionConfigConstructor orig_ephemeralSessionConfiguration; 46 | 47 | static SessionConfigConstructor OHHTTPStubsSwizzle(SEL selector, SessionConfigConstructor newImpl) 48 | { 49 | Class cls = NSURLSessionConfiguration.class; 50 | Class metaClass = object_getClass(cls); 51 | 52 | Method origMethod = class_getClassMethod(cls, selector); 53 | SessionConfigConstructor origImpl = (SessionConfigConstructor)method_getImplementation(origMethod); 54 | if (!class_addMethod(metaClass, selector, (IMP)newImpl, method_getTypeEncoding(origMethod))) 55 | { 56 | method_setImplementation(origMethod, (IMP)newImpl); 57 | } 58 | return origImpl; 59 | } 60 | 61 | static NSURLSessionConfiguration* OHHTTPStubs_defaultSessionConfiguration(id self, SEL _cmd) 62 | { 63 | NSURLSessionConfiguration* config = orig_defaultSessionConfiguration(self,_cmd); // call original method 64 | [OHHTTPStubs setEnabled:YES forSessionConfiguration:config]; //OHHTTPStubsAddProtocolClassToNSURLSessionConfiguration(config); 65 | return config; 66 | } 67 | 68 | static NSURLSessionConfiguration* OHHTTPStubs_ephemeralSessionConfiguration(id self, SEL _cmd) 69 | { 70 | NSURLSessionConfiguration* config = orig_ephemeralSessionConfiguration(self,_cmd); // call original method 71 | [OHHTTPStubs setEnabled:YES forSessionConfiguration:config]; //OHHTTPStubsAddProtocolClassToNSURLSessionConfiguration(config); 72 | return config; 73 | } 74 | 75 | @interface NSURLSessionConfiguration(OHHTTPStubsSupport) @end 76 | @implementation NSURLSessionConfiguration(OHHTTPStubsSupport) 77 | +(void)load 78 | { 79 | orig_defaultSessionConfiguration = OHHTTPStubsSwizzle(@selector(defaultSessionConfiguration), 80 | OHHTTPStubs_defaultSessionConfiguration); 81 | orig_ephemeralSessionConfiguration = OHHTTPStubsSwizzle(@selector(ephemeralSessionConfiguration), 82 | OHHTTPStubs_ephemeralSessionConfiguration); 83 | } 84 | @end 85 | 86 | #endif 87 | 88 | 89 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubs.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | * 3 | * Copyright (c) 2012 Olivier Halligon 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 | * 23 | ***********************************************************************************/ 24 | 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | #pragma mark - Imports 28 | 29 | #import 30 | 31 | #import 32 | #import 33 | 34 | 35 | NS_ASSUME_NONNULL_BEGIN 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | #pragma mark - Types 39 | 40 | typedef BOOL(^OHHTTPStubsTestBlock)(NSURLRequest* request); 41 | typedef OHHTTPStubsResponse* __nonnull (^OHHTTPStubsResponseBlock)( NSURLRequest* request); 42 | 43 | /** 44 | * This opaque type represents an installed stub and is used to uniquely 45 | * identify a stub once it has been created. 46 | * 47 | * This type is returned by the `stubRequestsPassingTest:withStubResponse:` method 48 | * so that you can later reference it and use this reference to remove the stub later. 49 | * 50 | * This type also let you add arbitrary metadata to a stub to differenciate it 51 | * more easily when debugging. 52 | */ 53 | @protocol OHHTTPStubsDescriptor 54 | /** 55 | * An arbitrary name that you can set and get to describe your stub. 56 | * Use it as your own convenience. 57 | * 58 | * This is especially useful if you dump all installed stubs using `allStubs` 59 | * or if you want to log which stubs are being triggered using `onStubActivation:`. 60 | */ 61 | @property(nonatomic, strong, nullable) NSString* name; 62 | @end 63 | 64 | //////////////////////////////////////////////////////////////////////////////// 65 | #pragma mark - Interface 66 | 67 | /** 68 | * Stubs Manager. Use this class to add and remove stubs and stub your network requests. 69 | */ 70 | @interface OHHTTPStubs : NSObject 71 | 72 | //////////////////////////////////////////////////////////////////////////////// 73 | #pragma mark - Adding & Removing stubs 74 | 75 | 76 | 77 | /** 78 | * Dedicated method to add a stub 79 | * 80 | * @param testBlock Block that should return `YES` if the request passed as parameter 81 | * should be stubbed with the response block, and `NO` if it should 82 | * hit the real world (or be managed by another stub). 83 | * @param responseBlock Block that will return the `OHHTTPStubsResponse` (response to 84 | * use for stubbing) corresponding to the given request 85 | * 86 | * @return a stub descriptor that uniquely identifies the stub and can be later used to remove it with `removeStub:`. 87 | * 88 | * @note The returned stub descriptor is retained (`__strong` reference) by `OHHTTPStubs` 89 | * until it is removed (with one of the `removeStub:` / `removeAllStubs` 90 | * methods); it is thus recommended to keep it in a `__weak` storage (and not `__strong`) 91 | * in your app code, to let the stub descriptor be destroyed and let the variable go 92 | * back to `nil` automatically when the stub is removed. 93 | */ 94 | +(id)stubRequestsPassingTest:(OHHTTPStubsTestBlock)testBlock 95 | withStubResponse:(OHHTTPStubsResponseBlock)responseBlock; 96 | 97 | /** 98 | * Remove a stub from the list of stubs 99 | * 100 | * @param stubDesc The stub descriptor that has been returned when adding the stub 101 | * using `stubRequestsPassingTest:withStubResponse:` 102 | * 103 | * @return `YES` if the stub has been successfully removed, `NO` if the parameter was 104 | * not a valid stub identifier 105 | */ 106 | +(BOOL)removeStub:(id)stubDesc; 107 | 108 | /** 109 | * Remove all the stubs from the stubs list. 110 | */ 111 | +(void)removeAllStubs; 112 | 113 | //////////////////////////////////////////////////////////////////////////////// 114 | #pragma mark - Disabling & Re-Enabling stubs 115 | 116 | /** 117 | * Enable or disable the stubs for the shared session or for `NSURLConnection` 118 | * 119 | * @param enabled If `YES`, enables the stubs. If `NO`, disable all the 120 | * stubs and let all the requests hit the real world. 121 | * 122 | * @note OHHTTPStubs are enabled by default, so there is no need to call 123 | * this method with `YES` for stubs to work, except if you explicitely 124 | * disabled the stubs before. 125 | * 126 | * @note This only affects requests that are further made using `NSURLConnection` 127 | * or using `[NSURLSession sharedSession]`. This does not affect requests 128 | * sent on an `NSURLSession` created using an `NSURLSessionConfiguration`. 129 | */ 130 | +(void)setEnabled:(BOOL)enabled; 131 | 132 | #if defined(__IPHONE_7_0) || defined(__MAC_10_9) 133 | /** 134 | * Enable or disable the stubs on a given `NSURLSessionConfiguration`. 135 | * 136 | * @param enabled If `YES`, enables the stubs for this `NSURLSessionConfiguration`. 137 | * If `NO`, disable the stubs and let all the requests hit the real world 138 | * @param sessionConfig The NSURLSessionConfiguration on which to enabled/disable the stubs 139 | * 140 | * @note OHHTTPStubs are enabled by default on newly created `defaultSessionConfiguration` 141 | * and `ephemeralSessionConfiguration`, so there is no need to call this method with 142 | * `YES` for stubs to work. You generally only use this if you want to disable 143 | * `OHTTPStubs` per `NSURLSession` by calling it before building the `NSURLSession` 144 | * with the `NSURLSessionConfiguration`. 145 | * 146 | * @note Important: As usual according to the way `NSURLSessionConfiguration` works, you 147 | * MUST set this property BEFORE creating the `NSURLSession`. Once the `NSURLSession` 148 | * object is created, they use a deep copy of the `NSURLSessionConfiguration` object 149 | * used to create them, so changing the configuration later does not affect already 150 | * created sessions. 151 | */ 152 | + (void)setEnabled:(BOOL)enabled forSessionConfiguration:(NSURLSessionConfiguration *)sessionConfig; 153 | #endif 154 | 155 | #pragma mark - Debug Methods 156 | 157 | /** 158 | * List all the installed stubs 159 | * 160 | * @return An array of `id` objects currently installed. Useful for debug. 161 | */ 162 | +(NSArray*)allStubs; 163 | 164 | /** 165 | * Setup a block to be called each time a stub is triggered. 166 | * 167 | * Useful if you want to log all your requests being stubbed for example and see which stub 168 | * was used to respond to each request. 169 | * 170 | * @param block The block to call each time a request is being stubbed by OHHTTPStubs. 171 | * Set it to `nil` to do nothing. Defaults is `nil`. 172 | */ 173 | +(void)onStubActivation:( void(^)(NSURLRequest* request, id stub) )block; 174 | 175 | @end 176 | 177 | NS_ASSUME_NONNULL_END 178 | 179 | 180 | //////////////////////////////////////////////////////////////////////////////// 181 | #pragma mark - Umbrella Header Imports 182 | 183 | 184 | #if ! __has_include() 185 | // Because this is supposed to be an umbrella header, we should also import every public headers here 186 | // (Except if we use already have a better umbrella header generated by CocoaPods) 187 | #if __has_include() 188 | #import 189 | #endif 190 | #if __has_include() 191 | #import 192 | #endif 193 | #if __has_include() 194 | #import 195 | #endif 196 | #if __has_include("OHHTTPStubs/OHPathHelpers.h") 197 | #import "OHHTTPStubs/OHPathHelpers.h" 198 | #endif 199 | #endif 200 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | * 3 | * Copyright (c) 2012 Olivier Halligon 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 | * 23 | ***********************************************************************************/ 24 | 25 | 26 | //////////////////////////////////////////////////////////////////////////////// 27 | #pragma mark - Imports 28 | 29 | #import 30 | #import 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | #pragma mark - Defines & Constants 34 | 35 | // Non-standard download speeds 36 | extern const double 37 | OHHTTPStubsDownloadSpeed1KBPS, // 1.0 KB per second 38 | OHHTTPStubsDownloadSpeedSLOW; // 1.5 KB per second 39 | 40 | // Standard download speeds. 41 | extern const double 42 | OHHTTPStubsDownloadSpeedGPRS, 43 | OHHTTPStubsDownloadSpeedEDGE, 44 | OHHTTPStubsDownloadSpeed3G, 45 | OHHTTPStubsDownloadSpeed3GPlus, 46 | OHHTTPStubsDownloadSpeedWifi; 47 | 48 | 49 | NS_ASSUME_NONNULL_BEGIN 50 | 51 | //////////////////////////////////////////////////////////////////////////////// 52 | #pragma mark - Interface 53 | 54 | /** 55 | * Stubs Response. This describes a stubbed response to be returned by the URL Loading System, 56 | * including its HTTP headers, body, statusCode and response time. 57 | */ 58 | @interface OHHTTPStubsResponse : NSObject 59 | 60 | //////////////////////////////////////////////////////////////////////////////// 61 | #pragma mark - Properties 62 | 63 | /** 64 | * The headers to use for the fake response 65 | */ 66 | @property(nonatomic, strong, nullable) NSDictionary* httpHeaders; 67 | /** 68 | * The HTTP status code to use for the fake response 69 | */ 70 | @property(nonatomic, assign) int statusCode; 71 | /** 72 | * The inputStream used when sending the response. 73 | * @note You generally don't manipulate this directly. 74 | */ 75 | @property(nonatomic, strong, nullable) NSInputStream* inputStream; 76 | /** 77 | * The size of the fake response body, in bytes. 78 | */ 79 | @property(nonatomic, assign) unsigned long long dataSize; 80 | /** 81 | * The duration to wait before faking receiving the response headers. 82 | * 83 | * Defaults to 0.0. 84 | */ 85 | @property(nonatomic, assign) NSTimeInterval requestTime; 86 | /** 87 | * The duration to use to send the fake response body. 88 | * 89 | * @note if responseTime<0, it is interpreted as a download speed in KBps ( -200 => 200KB/s ) 90 | */ 91 | @property(nonatomic, assign) NSTimeInterval responseTime; 92 | /** 93 | * The fake error to generate to simulate a network error. 94 | * 95 | * If `error` is non-`nil`, the request will result in a failure and no response will be sent. 96 | */ 97 | @property(nonatomic, strong, nullable) NSError* error; 98 | 99 | 100 | //////////////////////////////////////////////////////////////////////////////// 101 | #pragma mark - Commodity Constructors 102 | /*! @name Commodity */ 103 | 104 | /* -------------------------------------------------------------------------- */ 105 | #pragma mark > Building response from NSData 106 | 107 | /** 108 | * Builds a response given raw data. 109 | * 110 | * @note Internally calls `-initWithInputStream:dataSize:statusCode:headers:` with and inputStream built from the NSData. 111 | * 112 | * @param data The raw data to return in the response 113 | * @param statusCode The HTTP Status Code to use in the response 114 | * @param httpHeaders The HTTP Headers to return in the response 115 | * @return An `OHHTTPStubsResponse` describing the corresponding response to return by the stub 116 | */ 117 | +(instancetype)responseWithData:(NSData*)data 118 | statusCode:(int)statusCode 119 | headers:(nullable NSDictionary*)httpHeaders; 120 | 121 | 122 | /* -------------------------------------------------------------------------- */ 123 | #pragma mark > Building response from a file 124 | 125 | /** 126 | * Builds a response given a file path, the status code and headers. 127 | * 128 | * @param filePath The file path that contains the response body to return. 129 | * @param statusCode The HTTP Status Code to use in the response 130 | * @param httpHeaders The HTTP Headers to return in the response 131 | * 132 | * @return An `OHHTTPStubsResponse` describing the corresponding response to return by the stub 133 | * 134 | * @note It is encouraged to use the OHPathHelpers functions & macros to build 135 | * the filePath parameter easily 136 | */ 137 | +(instancetype)responseWithFileAtPath:(NSString *)filePath 138 | statusCode:(int)statusCode 139 | headers:(nullable NSDictionary*)httpHeaders; 140 | 141 | /* -------------------------------------------------------------------------- */ 142 | #pragma mark > Building an error response 143 | 144 | /** 145 | * Builds a response that corresponds to the given error 146 | * 147 | * @param error The error to use in the stubbed response. 148 | * 149 | * @return An `OHHTTPStubsResponse` describing the corresponding response to return by the stub 150 | * 151 | * @note For example you could use an error like `[NSError errorWithDomain:NSURLErrorDomain code:kCFURLErrorNotConnectedToInternet userInfo:nil]` 152 | */ 153 | +(instancetype)responseWithError:(NSError*)error; 154 | 155 | 156 | //////////////////////////////////////////////////////////////////////////////// 157 | #pragma mark - Commotidy Setters 158 | 159 | /** 160 | * Set the `responseTime` of the `OHHTTPStubsResponse` and return `self`. Useful for chaining method calls. 161 | * 162 | * _Usage example:_ 163 | *
return [[OHHTTPStubsReponse responseWithData:data statusCode:200 headers:nil] responseTime:5.0];
164 | * 165 | * @param responseTime If positive, the amount of time used to send the entire response. 166 | * If negative, the rate in KB/s at which to send the response data. 167 | * Useful to simulate slow networks for example. You may use the 168 | * _OHHTTPStubsDownloadSpeed…_ constants here. 169 | * 170 | * @return `self` (= the same `OHHTTPStubsResponse` that was the target of this method). 171 | * Returning `self` is useful for chaining method calls. 172 | */ 173 | -(instancetype)responseTime:(NSTimeInterval)responseTime; 174 | 175 | /** 176 | * Set both the `requestTime` and the `responseTime` of the `OHHTTPStubsResponse` at once. 177 | * Useful for chaining method calls. 178 | * 179 | * _Usage example:_ 180 | *
return [[OHHTTPStubsReponse responseWithData:data statusCode:200 headers:nil]
181 |  *            requestTime:1.0 responseTime:5.0];
182 | * 183 | * @param requestTime The time to wait before the response begins to send. This value must be greater than or equal to zero. 184 | * @param responseTime If positive, the amount of time used to send the entire response. 185 | * If negative, the rate in KB/s at which to send the response data. 186 | * Useful to simulate slow networks for example. You may use the 187 | * _OHHTTPStubsDownloadSpeed…_ constants here. 188 | * 189 | * @return `self` (= the same `OHHTTPStubsResponse` that was the target of this method). Useful for chaining method calls. 190 | */ 191 | -(instancetype)requestTime:(NSTimeInterval)requestTime responseTime:(NSTimeInterval)responseTime; 192 | 193 | 194 | //////////////////////////////////////////////////////////////////////////////// 195 | #pragma mark - Initializers 196 | /*! @name Initializers */ 197 | 198 | /** 199 | * Designated empty initializer 200 | * 201 | * @return An empty `OHHTTPStubsResponse` on which you need to set either an error or a statusCode, httpHeaders, inputStream and dataSize. 202 | * 203 | * @note This is not recommended to use this method directly. You should use `initWithInputStream:dataSize:statusCode:headers:` instead. 204 | */ 205 | -(instancetype)init NS_DESIGNATED_INITIALIZER; 206 | 207 | /** 208 | * Designed initializer. Initialize a response with the given input stream, dataSize, 209 | * statusCode and headers. 210 | * 211 | * @param inputStream The input stream that will provide the data to return in the response 212 | * @param dataSize The size of the data in the stream. 213 | * @param statusCode The HTTP Status Code to use in the response 214 | * @param httpHeaders The HTTP Headers to return in the response 215 | * 216 | * @return An `OHHTTPStubsResponse` describing the corresponding response to return by the stub 217 | * 218 | * @note You will probably never need to call this method yourself. Prefer the other initializers (that will call this method eventually) 219 | */ 220 | -(instancetype)initWithInputStream:(NSInputStream*)inputStream 221 | dataSize:(unsigned long long)dataSize 222 | statusCode:(int)statusCode 223 | headers:(nullable NSDictionary*)httpHeaders NS_DESIGNATED_INITIALIZER; 224 | 225 | 226 | /** 227 | * Initialize a response with a given file path, statusCode and headers. 228 | * 229 | * @param filePath The file path of the data to return in the response 230 | * @param statusCode The HTTP Status Code to use in the response 231 | * @param httpHeaders The HTTP Headers to return in the response 232 | * 233 | * @return An `OHHTTPStubsResponse` describing the corresponding response to return by the stub 234 | * 235 | * @note This method simply builds the NSInputStream, compute the file size, and then call `-initWithInputStream:dataSize:statusCode:headers:` 236 | */ 237 | -(instancetype)initWithFileAtPath:(NSString*)filePath 238 | statusCode:(int)statusCode 239 | headers:(nullable NSDictionary*)httpHeaders; 240 | 241 | 242 | /** 243 | * Initialize a response with the given data, statusCode and headers. 244 | * 245 | * @param data The raw data to return in the response 246 | * @param statusCode The HTTP Status Code to use in the response 247 | * @param httpHeaders The HTTP Headers to return in the response 248 | * 249 | * @return An `OHHTTPStubsResponse` describing the corresponding response to return by the stub 250 | */ 251 | -(instancetype)initWithData:(NSData*)data 252 | statusCode:(int)statusCode 253 | headers:(nullable NSDictionary*)httpHeaders; 254 | 255 | 256 | /** 257 | * Designed initializer. Initialize a response with the given error. 258 | * 259 | * @param error The error to use in the stubbed response. 260 | * 261 | * @return An `OHHTTPStubsResponse` describing the corresponding response to return by the stub 262 | * 263 | * @note For example you could use an error like `[NSError errorWithDomain:NSURLErrorDomain code:kCFURLErrorNotConnectedToInternet userInfo:nil]` 264 | */ 265 | -(instancetype)initWithError:(NSError*)error NS_DESIGNATED_INITIALIZER; 266 | 267 | @end 268 | 269 | NS_ASSUME_NONNULL_END 270 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/OHHTTPStubs/Sources/OHHTTPStubsResponse.m: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | * 3 | * Copyright (c) 2012 Olivier Halligon 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 | * 23 | ***********************************************************************************/ 24 | 25 | #if ! __has_feature(objc_arc) 26 | #error This file is expected to be compiled with ARC turned ON 27 | #endif 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | #pragma mark - Imports 31 | 32 | #import 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | #pragma mark - Defines & Constants 36 | const double OHHTTPStubsDownloadSpeed1KBPS =- 8 / 8; // kbps -> KB/s 37 | const double OHHTTPStubsDownloadSpeedSLOW =- 12 / 8; // kbps -> KB/s 38 | const double OHHTTPStubsDownloadSpeedGPRS =- 56 / 8; // kbps -> KB/s 39 | const double OHHTTPStubsDownloadSpeedEDGE =- 128 / 8; // kbps -> KB/s 40 | const double OHHTTPStubsDownloadSpeed3G =- 3200 / 8; // kbps -> KB/s 41 | const double OHHTTPStubsDownloadSpeed3GPlus =- 7200 / 8; // kbps -> KB/s 42 | const double OHHTTPStubsDownloadSpeedWifi =- 12000 / 8; // kbps -> KB/s 43 | 44 | //////////////////////////////////////////////////////////////////////////////// 45 | #pragma mark - Implementation 46 | 47 | @implementation OHHTTPStubsResponse 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | #pragma mark - Commodity Constructors 51 | 52 | 53 | #pragma mark > Building response from NSData 54 | 55 | +(instancetype)responseWithData:(NSData*)data 56 | statusCode:(int)statusCode 57 | headers:(nullable NSDictionary*)httpHeaders 58 | { 59 | OHHTTPStubsResponse* response = [[self alloc] initWithData:data 60 | statusCode:statusCode 61 | headers:httpHeaders]; 62 | return response; 63 | } 64 | 65 | 66 | #pragma mark > Building response from a file 67 | 68 | +(instancetype)responseWithFileAtPath:(NSString *)filePath 69 | statusCode:(int)statusCode 70 | headers:(nullable NSDictionary *)httpHeaders 71 | { 72 | OHHTTPStubsResponse* response = [[self alloc] initWithFileAtPath:filePath 73 | statusCode:statusCode 74 | headers:httpHeaders]; 75 | return response; 76 | } 77 | 78 | 79 | #pragma mark > Building an error response 80 | 81 | +(instancetype)responseWithError:(NSError*)error 82 | { 83 | OHHTTPStubsResponse* response = [[self alloc] initWithError:error]; 84 | return response; 85 | } 86 | 87 | //////////////////////////////////////////////////////////////////////////////// 88 | #pragma mark - Commotidy Setters 89 | 90 | -(instancetype)responseTime:(NSTimeInterval)responseTime 91 | { 92 | _responseTime = responseTime; 93 | return self; 94 | } 95 | 96 | -(instancetype)requestTime:(NSTimeInterval)requestTime responseTime:(NSTimeInterval)responseTime 97 | { 98 | _requestTime = requestTime; 99 | _responseTime = responseTime; 100 | return self; 101 | } 102 | 103 | //////////////////////////////////////////////////////////////////////////////// 104 | #pragma mark - Initializers 105 | 106 | -(instancetype)init 107 | { 108 | self = [super init]; 109 | return self; 110 | } 111 | 112 | -(instancetype)initWithInputStream:(NSInputStream*)inputStream 113 | dataSize:(unsigned long long)dataSize 114 | statusCode:(int)statusCode 115 | headers:(nullable NSDictionary*)httpHeaders 116 | { 117 | self = [super init]; 118 | if (self) 119 | { 120 | _inputStream = inputStream; 121 | _dataSize = dataSize; 122 | _statusCode = statusCode; 123 | NSMutableDictionary * headers = [NSMutableDictionary dictionaryWithDictionary:httpHeaders]; 124 | static NSString *const ContentLengthHeader = @"Content-Length"; 125 | if (!headers[ContentLengthHeader]) 126 | { 127 | headers[ContentLengthHeader] = [NSString stringWithFormat:@"%llu",_dataSize]; 128 | } 129 | _httpHeaders = [NSDictionary dictionaryWithDictionary:headers]; 130 | } 131 | return self; 132 | } 133 | 134 | -(instancetype)initWithFileAtPath:(NSString*)filePath 135 | statusCode:(int)statusCode 136 | headers:(nullable NSDictionary*)httpHeaders 137 | { 138 | NSInputStream* inputStream; 139 | if (filePath) 140 | { 141 | inputStream = [NSInputStream inputStreamWithFileAtPath:filePath]; 142 | } 143 | else 144 | { 145 | NSLog(@"%s: nil file path. Returning empty data", __PRETTY_FUNCTION__); 146 | inputStream = [NSInputStream inputStreamWithData:[NSData data]]; 147 | } 148 | 149 | NSDictionary* attributes = [NSFileManager.defaultManager attributesOfItemAtPath:filePath error:nil]; 150 | unsigned long long fileSize = [[attributes valueForKey:NSFileSize] unsignedLongLongValue]; 151 | self = [self initWithInputStream:inputStream 152 | dataSize:fileSize 153 | statusCode:statusCode 154 | headers:httpHeaders]; 155 | return self; 156 | } 157 | 158 | -(instancetype)initWithData:(NSData*)data 159 | statusCode:(int)statusCode 160 | headers:(nullable NSDictionary*)httpHeaders 161 | { 162 | NSInputStream* inputStream = [NSInputStream inputStreamWithData:data?:[NSData data]]; 163 | self = [self initWithInputStream:inputStream 164 | dataSize:data.length 165 | statusCode:statusCode 166 | headers:httpHeaders]; 167 | return self; 168 | } 169 | 170 | -(instancetype)initWithError:(NSError*)error 171 | { 172 | self = [super init]; 173 | if (self) { 174 | _error = error; 175 | } 176 | return self; 177 | } 178 | 179 | -(NSString*)debugDescription 180 | { 181 | return [NSString stringWithFormat:@"<%@ %p requestTime:%f responseTime:%f status:%d dataSize:%llu>", 182 | self.class, self, self.requestTime, self.responseTime, self.statusCode, self.dataSize]; 183 | } 184 | 185 | //////////////////////////////////////////////////////////////////////////////// 186 | #pragma mark - Accessors 187 | 188 | -(void)setRequestTime:(NSTimeInterval)requestTime 189 | { 190 | NSAssert(requestTime >= 0, @"Invalid Request Time (%f) for OHHTTPStubResponse. Request time must be greater than or equal to zero",requestTime); 191 | _requestTime = requestTime; 192 | } 193 | 194 | @end 195 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.h: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | * 3 | * Copyright (c) 2012 Olivier Halligon 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 | * 23 | ***********************************************************************************/ 24 | 25 | 26 | #import 27 | #import 28 | 29 | NS_ASSUME_NONNULL_BEGIN 30 | 31 | /** 32 | * Useful function to build a path given a file name and a class. 33 | * 34 | * @param fileName The name of the file to get the path to, including file extension 35 | * @param inBundleForClass The class of the caller, used to determine the current bundle 36 | * in which the file is supposed to be located. 37 | * You should typically pass `self.class` (ObjC) or 38 | * `self.dynamicType` (Swift) when calling this function. 39 | * 40 | * @return The path of the given file in the same bundle as the inBundleForClass class 41 | */ 42 | NSString* __nullable OHPathForFile(NSString* fileName, Class inBundleForClass); 43 | 44 | /** 45 | * Useful function to build a path given a file name and a bundle. 46 | * 47 | * @param fileName The name of the file to get the path to, including file extension 48 | * @param bundle The bundle in which the file is supposed to be located. 49 | * This parameter can't be null. 50 | * 51 | * @return The path of the given file in given bundle 52 | * 53 | * @note You should avoid using `[NSBundle mainBundle]` for the `bundle` parameter, 54 | * as in the context of Unit Tests, this points to the Simulator's bundle, 55 | * not the bundle of the app under test. That's why `nil` is not an acceptable 56 | * value (so you won't expect it to default to the `mainBundle`). 57 | * You should use `[NSBundle bundleForClass:]` instead. 58 | */ 59 | NSString* __nullable OHPathForFileInBundle(NSString* fileName, NSBundle* bundle); 60 | 61 | /** 62 | * Useful function to build a path to a file in the Documents's directory in the 63 | * app sandbox, used by iTunes File Sharing for example. 64 | * 65 | * @param fileName The name of the file to get the path to, including file extension 66 | * 67 | * @return The path of the file in the Documents directory in your App Sandbox 68 | */ 69 | NSString* __nullable OHPathForFileInDocumentsDir(NSString* fileName); 70 | 71 | 72 | 73 | /** 74 | * Useful function to build an NSBundle located in the application's resources simply from its name 75 | * 76 | * @param bundleBasename The base name, without extension (extension is assumed to be ".bundle"). 77 | * @param inBundleForClass The class of the caller, used to determine the current bundle 78 | * in which the file is supposed to be located. 79 | * You should typically pass `self.class` (ObjC) or 80 | * `self.dynamicType` (Swift) when calling this function. 81 | * 82 | * @return The NSBundle object representing the bundle with the given basename located in your application's resources. 83 | */ 84 | NSBundle* __nullable OHResourceBundle(NSString* bundleBasename, Class inBundleForClass); 85 | 86 | NS_ASSUME_NONNULL_END 87 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/OHHTTPStubs/Sources/OHPathHelpers/OHPathHelpers.m: -------------------------------------------------------------------------------- 1 | /*********************************************************************************** 2 | * 3 | * Copyright (c) 2012 Olivier Halligon 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 | * 23 | ***********************************************************************************/ 24 | 25 | 26 | #import 27 | 28 | NSString* __nullable OHPathForFile(NSString* fileName, Class inBundleForClass) 29 | { 30 | NSBundle* bundle = [NSBundle bundleForClass:inBundleForClass]; 31 | return OHPathForFileInBundle(fileName, bundle); 32 | } 33 | 34 | NSString* __nullable OHPathForFileInBundle(NSString* fileName, NSBundle* bundle) 35 | { 36 | return [bundle pathForResource:[fileName stringByDeletingPathExtension] 37 | ofType:[fileName pathExtension]]; 38 | } 39 | 40 | NSString* __nullable OHPathForFileInDocumentsDir(NSString* fileName) 41 | { 42 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 43 | NSString *basePath = (paths.count > 0) ? paths[0] : nil; 44 | return [basePath stringByAppendingPathComponent:fileName]; 45 | } 46 | 47 | NSBundle* __nullable OHResourceBundle(NSString* bundleBasename, Class inBundleForClass) 48 | { 49 | NSBundle* classBundle = [NSBundle bundleForClass:inBundleForClass]; 50 | return [NSBundle bundleWithPath:[classBundle pathForResource:bundleBasename 51 | ofType:@"bundle"]]; 52 | } 53 | -------------------------------------------------------------------------------- /Pods/OHHTTPStubs/README.md: -------------------------------------------------------------------------------- 1 | OHHTTPStubs 2 | =========== 3 | 4 | [![Platform](http://cocoapod-badges.herokuapp.com/p/OHHTTPStubs/badge.png)](http://cocoadocs.org/docsets/OHHTTPStubs) 5 | [![Version](http://cocoapod-badges.herokuapp.com/v/OHHTTPStubs/badge.png)](http://cocoadocs.org/docsets/OHHTTPStubs) 6 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 7 | [![Build Status](https://travis-ci.org/AliSoftware/OHHTTPStubs.png?branch=master)](https://travis-ci.org/AliSoftware/OHHTTPStubs) 8 | 9 | `OHHTTPStubs` is a library designed to stub your network requests very easily. It can help you: 10 | 11 | * test your apps with **fake network data** (stubbed from file) and **simulate slow networks**, to check your application behavior in bad network conditions 12 | * write **Unit Tests** that use fake network data from your fixtures. 13 | 14 | It works with `NSURLConnection`, new iOS7/OSX.9's `NSURLSession`, `AFNetworking` (both 1.x and 2.x), or any networking framework that use Cocoa's URL Loading System. 15 | 16 | [![Donate](http://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TRTU3UEWEHV92 "Donate") 17 | 18 | ---- 19 | 20 | # Documentation & Usage Examples 21 | 22 | `OHHTTPStubs` headers are fully documented using Appledoc-like / Headerdoc-like comments in the header files. You can also [read the **online documentation** here](http://cocoadocs.org/docsets/OHHTTPStubs) 23 | [![Version](http://cocoapod-badges.herokuapp.com/v/OHHTTPStubs/badge.png)](http://cocoadocs.org/docsets/OHHTTPStubs) 24 | 25 | ## Swift support 26 | 27 | `OHHTTPStubs` is compatible with Swift out of the box: you can use it with the same API as you would use in Objective-C. But you might also want to include the `OHHTTPStubs/Swift` subspec in your `Podfile`, which adds some global function helpers (see `OHHTTPStubsSwift.swift`) to make the use of `OHHTTPStubs` more compact and Swift-like. 28 | 29 | ## Basic example 30 | 31 | ### In Objective-C 32 | 33 | ```objc 34 | [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { 35 | return [request.URL.host isEqualToString:@"mywebservice.com"]; 36 | } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { 37 | // Stub it with our "wsresponse.json" stub file (which is in same bundle as self) 38 | NSString* fixture = OHPathForFile(@"wsresponse.json", self.class); 39 | return [OHHTTPStubsResponse responseWithFileAtPath:fixture 40 | statusCode:200 headers:@{@"Content-Type":@"application/json"}]; 41 | }]; 42 | ``` 43 | 44 | ### In Swift 45 | 46 | This example is using the Swift helpers found in `OHHTTPStubsSwift.swift` provided by the `OHHTTPStubs/Swift` subspec 47 | 48 | ```swift 49 | stub(isHost("mywebservice.com")) { _ in 50 | // Stub it with our "wsresponse.json" stub file (which is in same bundle as self) 51 | let stubPath = OHPathForFile("wsresponse.json", self.dynamicType) 52 | return fixture(stubPath!, headers: ["Content-Type":"application/json"]) 53 | } 54 | ``` 55 | 56 | Note: Using `OHHTTPStubsSwift.swift` you could also compose the matcher functions like this: `stub(isScheme("http") && isHost("myhost")) { … }` 57 | 58 | ## More examples & Help Topics 59 | 60 | * For a lot more examples, see the dedicated "[Usage Examples](https://github.com/AliSoftware/OHHTTPStubs/wiki/Usage-Examples)" wiki page. 61 | * The wiki also contain [some articles that can help you get started](https://github.com/AliSoftware/OHHTTPStubs/wiki) with (and troubleshoot if needed) `OHHTTPStubs`. 62 | 63 | # Compatibility 64 | 65 | `OHHTTPStubs` is compatible with **iOS 5.0+** and **OSX 10.7+**. 66 | 67 | `OHHTTPStubs` also works with iOS7's and OSX 10.9's `NSURLSession` mechanism. 68 | 69 | `OHHTTPStubs` is fully **Swift-compatible**. [Nullability annotations](https://developer.apple.com/swift/blog/?id=25) have been added to allow a cleaner API when used from Swift. 70 | 71 | # Installing in your projects 72 | 73 | Using [CocoaPods](https://guides.cocoapods.org) is the recommended way. 74 | Simply add `pod 'OHHTTPStubs'` to your `Podfile`. 75 | 76 | _`OHHTTPStubs` should also be compatible with Carthage — but I won't guarantee help/support for it as I don't use it personally._ 77 | 78 | # Special Considerations 79 | 80 | ## Using OHHTTPStubs in your Unit Tests 81 | 82 | `OHHTTPStubs` is ideal to write Unit Tests that normally would perform network requests. But if you use it in your Unit Tests, don't forget to: 83 | 84 | * remove any stubs you installed after each test — to avoid those stubs to still be installed when executing the next Test Case — by calling `[OHHTTPStubs removeAllStubs]` in your `tearDown` method. [see this wiki page for more info](https://github.com/AliSoftware/OHHTTPStubs/wiki/Remove-stubs-after-each-test) 85 | * be sure to wait until the request has received its response before doing your assertions and letting the test case finish (like for any asynchronous test). [see this wiki page for more info](https://github.com/AliSoftware/OHHTTPStubs/wiki/OHHTTPStubs-and-asynchronous-tests) 86 | 87 | ## Automatic loading 88 | 89 | Thanks to method swizzling, `OHHTTPStubs` is automatically loaded and installed both for: 90 | 91 | * requests made using `NSURLConnection` or `[NSURLSession sharedSession]`; 92 | * requests made using a `NSURLSession` created using a `[NSURLSessionConfiguration defaultSessionConfiguration]` or `[NSURLSessionConfiguration ephemeralSessionConfiguration]` configuration (using `[NSURLSession sessionWithConfiguration:…]`-like methods). 93 | 94 | If you need to disable (and re-enable) `OHHTTPStubs` — globally or per `NSURLSession` — you can use `[OHHTTPStubs setEnabled:]` / `[OHHTTPStubs setEnabled:forSessionConfiguration:]`. 95 | 96 | ## Known limitations 97 | 98 | * `OHHTTPStubs` **can't work on background sessions** (sessions created using `[NSURLSessionConfiguration backgroundSessionConfiguration]`) because background sessions don't allow the use of custom `NSURLProtocols` and are handled by the iOS Operating System itself. 99 | * `OHHTTPStubs` don't simulate data upload. The `NSURLProtocolClient` `@protocol` does not provide a way to signal the delegate that data has been **sent** (only that some has been loaded), so any data in the `HTTPBody` or `HTTPBodyStream` of an `NSURLRequest`, or data provided to `-[NSURLSession uploadTaskWithRequest:fromData:];` will be ignored, and more importantly, the `-URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:` delegate method will never be called when you stub the request using `OHHTTPStubs`. 100 | 101 | _As far as I know, there's nothing we can do about those two limitations. Please let me know if you know a solution that would make that possible anyway._ 102 | 103 | 104 | ## Submitting to the AppStore 105 | 106 | `OHHTTPStubs` **can be used** on apps submitted **on the AppStore**. It does not use any private API and nothing prevents you from shipping it. 107 | 108 | But you generally only use stubs during the development phase and want to remove your stubs when submitting to the AppStore. So be careful to only include `OHHTTPStubs` when needed (only in your test targets, or only inside `#if DEBUG` portions, or by using [per-Build-Configuration pods](https://guides.cocoapods.org/syntax/podfile.html#pod)) to avoid forgetting to remove it when the time comes that you release for the AppStore and you want your requests to hit the net! 109 | 110 | 111 | 112 | # License and Credits 113 | 114 | This project and library has been created by Olivier Halligon (@aligatr on Twitter) and is under the MIT License. 115 | 116 | It has been inspired by [this article from InfiniteLoop.dk](http://www.infinite-loop.dk/blog/2011/09/using-nsurlprotocol-for-injecting-test-data/). 117 | 118 | I would also like to thank Kevin Harwood ([@kcharwood](https://github.com/kcharwood)) for migrating the code to `NSInputStream`, Jinlian Wang ([@JinlianWang](https://github.com/JinlianWang)) for adding Mocktail support, and everyone else who contributed to this project on GitHub somehow. 119 | 120 | If you want to support the development of this library, feel free to [![Donate](http://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TRTU3UEWEHV92 "Donate"). Thanks to all contributors so far! 121 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_FBSnapshotTestCase : NSObject 3 | @end 4 | @implementation PodsDummy_FBSnapshotTestCase 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "FBSnapshotTestCase.h" 4 | #import "FBSnapshotTestCasePlatform.h" 5 | #import "UIImage+Compare.h" 6 | #import "UIImage+Diff.h" 7 | #import "UIImage+Snapshot.h" 8 | #import "FBSnapshotTestCase.h" 9 | #import "FBSnapshotTestCasePlatform.h" 10 | #import "FBSnapshotTestController.h" 11 | 12 | FOUNDATION_EXPORT double FBSnapshotTestCaseVersionNumber; 13 | FOUNDATION_EXPORT const unsigned char FBSnapshotTestCaseVersionString[]; 14 | 15 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.modulemap: -------------------------------------------------------------------------------- 1 | framework module FBSnapshotTestCase { 2 | umbrella header "FBSnapshotTestCase-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | 7 | private header "FBSnapshotTestController.h" 8 | } 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/FBSnapshotTestCase/FBSnapshotTestCase.xcconfig: -------------------------------------------------------------------------------- 1 | ENABLE_BITCODE = NO 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$(PLATFORM_DIR)/Developer/Library/Frameworks" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/FBSnapshotTestCase" "${PODS_ROOT}/Headers/Public" 5 | OTHER_LDFLAGS = -framework "XCTest" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_ROOT = ${SRCROOT} 8 | SKIP_INSTALL = YES -------------------------------------------------------------------------------- /Pods/Target Support Files/FBSnapshotTestCase/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.0.4 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/OHHTTPStubs/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.3.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_OHHTTPStubs : NSObject 3 | @end 4 | @implementation PodsDummy_OHHTTPStubs 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "Compatibility.h" 4 | #import "OHHTTPStubs.h" 5 | #import "OHHTTPStubsResponse.h" 6 | #import "OHHTTPStubsResponse+JSON.h" 7 | #import "OHPathHelpers.h" 8 | #import "Compatibility.h" 9 | 10 | FOUNDATION_EXPORT double OHHTTPStubsVersionNumber; 11 | FOUNDATION_EXPORT const unsigned char OHHTTPStubsVersionString[]; 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.modulemap: -------------------------------------------------------------------------------- 1 | framework module OHHTTPStubs { 2 | umbrella header "OHHTTPStubs-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/OHHTTPStubs/OHHTTPStubs.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/OHHTTPStubs" "${PODS_ROOT}/Headers/Public" 3 | OTHER_LDFLAGS = -framework "CFNetwork" -framework "Foundation" 4 | PODS_ROOT = ${SRCROOT} 5 | SKIP_INSTALL = YES -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Pods-GooglePlacesAutocompleteExampleTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## FBSnapshotTestCase 5 | 6 | BSD License 7 | 8 | For the FBSnapshotTestCase software 9 | 10 | Copyright (c) 2013, Facebook, Inc. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | * Redistributions in binary form must reproduce the above copyright notice, 19 | this list of conditions and the following disclaimer in the documentation 20 | and/or other materials provided with the distribution. 21 | * Neither the name Facebook nor the names of its contributors may be used to 22 | endorse or promote products derived from this software without specific 23 | prior written permission. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 26 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 29 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | 36 | 37 | ## OHHTTPStubs 38 | 39 | - MIT LICENSE - 40 | 41 | Copyright (c) 2012 Olivier Halligon 42 | 43 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 46 | 47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | Generated by CocoaPods - http://cocoapods.org 49 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Pods-GooglePlacesAutocompleteExampleTests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | BSD License 18 | 19 | For the FBSnapshotTestCase software 20 | 21 | Copyright (c) 2013, Facebook, Inc. 22 | All rights reserved. 23 | 24 | Redistribution and use in source and binary forms, with or without 25 | modification, are permitted provided that the following conditions are met: 26 | 27 | * Redistributions of source code must retain the above copyright notice, 28 | this list of conditions and the following disclaimer. 29 | * Redistributions in binary form must reproduce the above copyright notice, 30 | this list of conditions and the following disclaimer in the documentation 31 | and/or other materials provided with the distribution. 32 | * Neither the name Facebook nor the names of its contributors may be used to 33 | endorse or promote products derived from this software without specific 34 | prior written permission. 35 | 36 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 37 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 38 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 39 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 40 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 41 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 42 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 43 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 44 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 46 | 47 | Title 48 | FBSnapshotTestCase 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | FooterText 54 | - MIT LICENSE - 55 | 56 | Copyright (c) 2012 Olivier Halligon 57 | 58 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 59 | 60 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 61 | 62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 63 | Title 64 | OHHTTPStubs 65 | Type 66 | PSGroupSpecifier 67 | 68 | 69 | FooterText 70 | Generated by CocoaPods - http://cocoapods.org 71 | Title 72 | 73 | Type 74 | PSGroupSpecifier 75 | 76 | 77 | StringsTable 78 | Acknowledgements 79 | Title 80 | Acknowledgements 81 | 82 | 83 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Pods-GooglePlacesAutocompleteExampleTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_GooglePlacesAutocompleteExampleTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_GooglePlacesAutocompleteExampleTests 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Pods-GooglePlacesAutocompleteExampleTests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | 86 | if [[ "$CONFIGURATION" == "Debug" ]]; then 87 | install_framework "Pods-GooglePlacesAutocompleteExampleTests/FBSnapshotTestCase.framework" 88 | install_framework "Pods-GooglePlacesAutocompleteExampleTests/OHHTTPStubs.framework" 89 | fi 90 | if [[ "$CONFIGURATION" == "Release" ]]; then 91 | install_framework "Pods-GooglePlacesAutocompleteExampleTests/FBSnapshotTestCase.framework" 92 | install_framework "Pods-GooglePlacesAutocompleteExampleTests/OHHTTPStubs.framework" 93 | fi 94 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Pods-GooglePlacesAutocompleteExampleTests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | realpath() { 12 | DIRECTORY="$(cd "${1%/*}" && pwd)" 13 | FILENAME="${1##*/}" 14 | echo "$DIRECTORY/$FILENAME" 15 | } 16 | 17 | install_resource() 18 | { 19 | case $1 in 20 | *.storyboard) 21 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 22 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 23 | ;; 24 | *.xib) 25 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 26 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 27 | ;; 28 | *.framework) 29 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 30 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 31 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 32 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 33 | ;; 34 | *.xcdatamodel) 35 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 36 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 37 | ;; 38 | *.xcdatamodeld) 39 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 40 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 41 | ;; 42 | *.xcmappingmodel) 43 | echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" 44 | xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" 45 | ;; 46 | *.xcassets) 47 | ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") 48 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 49 | ;; 50 | /*) 51 | echo "$1" 52 | echo "$1" >> "$RESOURCES_TO_COPY" 53 | ;; 54 | *) 55 | echo "${PODS_ROOT}/$1" 56 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 57 | ;; 58 | esac 59 | } 60 | 61 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 62 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 63 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 64 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 65 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 66 | fi 67 | rm -f "$RESOURCES_TO_COPY" 68 | 69 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 70 | then 71 | case "${TARGETED_DEVICE_FAMILY}" in 72 | 1,2) 73 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 74 | ;; 75 | 1) 76 | TARGET_DEVICE_ARGS="--target-device iphone" 77 | ;; 78 | 2) 79 | TARGET_DEVICE_ARGS="--target-device ipad" 80 | ;; 81 | *) 82 | TARGET_DEVICE_ARGS="--target-device mac" 83 | ;; 84 | esac 85 | 86 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 87 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 88 | while read line; do 89 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 90 | XCASSET_FILES+=("$line") 91 | fi 92 | done <<<"$OTHER_XCASSETS" 93 | 94 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 95 | fi 96 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Pods-GooglePlacesAutocompleteExampleTests-umbrella.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | 4 | FOUNDATION_EXPORT double Pods_GooglePlacesAutocompleteExampleTestsVersionNumber; 5 | FOUNDATION_EXPORT const unsigned char Pods_GooglePlacesAutocompleteExampleTestsVersionString[]; 6 | 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Pods-GooglePlacesAutocompleteExampleTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/FBSnapshotTestCase.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/OHHTTPStubs.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase" -framework "OHHTTPStubs" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-GooglePlacesAutocompleteExampleTests 8 | PODS_ROOT = ${SRCROOT}/../Pods -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Pods-GooglePlacesAutocompleteExampleTests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_GooglePlacesAutocompleteExampleTests { 2 | umbrella header "Pods-GooglePlacesAutocompleteExampleTests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GooglePlacesAutocompleteExampleTests/Pods-GooglePlacesAutocompleteExampleTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/FBSnapshotTestCase.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/OHHTTPStubs.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "FBSnapshotTestCase" -framework "OHHTTPStubs" 6 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 7 | PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods-GooglePlacesAutocompleteExampleTests 8 | PODS_ROOT = ${SRCROOT}/../Pods -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GooglePlacesAutocomplete 2 | 3 | [![Build Status](http://img.shields.io/travis/watsonbox/ios_google_places_autocomplete.svg?style=flat)](https://travis-ci.org/watsonbox/ios_google_places_autocomplete) 4 | [![CocoaPod](https://img.shields.io/cocoapods/v/GooglePlacesAutocomplete.svg)](http://cocoadocs.org/docsets/GooglePlacesAutocomplete/) 5 | 6 | A simple [Google Places API](https://developers.google.com/places/documentation/autocomplete) autocompleting address entry view for iOS devices. 7 | 8 | There are already a couple of solutions out there for this. GooglePlacesAutocomplete is different because it is 100% Swift, and aims to provide the simplest possible method of entering validated, autocompleted addresses. 9 | 10 | No attempt has been made to integrate MapKit since displaying Google Places on a non-Google map is against their terms of service. 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | ---------- 20 | 21 | 22 | ## Requirements 23 | 24 | - iOS 7.0+ 25 | - XCode 7.0+ / Swift 2.0 26 | 27 | ## Installation 28 | 29 | > **Embedded frameworks require a minimum deployment target of iOS 8.** 30 | > 31 | > To use GooglePlacesAutocomplete with a project targeting iOS 7, you must include the source files directly in your project. See the ['manual installation'](#manual) section for instructions. 32 | 33 | ### CocoaPods 34 | 35 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. 36 | 37 | CocoaPods 0.36 [adds supports](http://blog.cocoapods.org/CocoaPods-0.36/) for Swift and embedded frameworks. You can install it with the following command: 38 | 39 | ```bash 40 | $ gem install cocoapods 41 | ``` 42 | 43 | To integrate GooglePlacesAutocomplete into your Xcode project using CocoaPods, specify it in your `Podfile`: 44 | 45 | ```ruby 46 | source 'https://github.com/CocoaPods/Specs.git' 47 | platform :ios, '8.0' 48 | 49 | pod 'GooglePlacesAutocomplete' 50 | ``` 51 | 52 | Then, run the following command: 53 | 54 | ```bash 55 | $ pod install 56 | ``` 57 | 58 | ### Manual 59 | 60 | Simply copy `GooglePlacesAutocomplete.swift` and `GooglePlacesAutocomplete.xib` to your project. 61 | 62 | Note: Don't forget to add the PoweredByGoogle image to your xcassets. 63 | 64 | 65 | ## Usage 66 | 67 | Use the [Google Developers Console](https://console.developers.google.com/) to enabled the 'Google Places API Web Service' and create a 'Server' API key credential. In both cases do *not* use the iOS options. 68 | 69 | ```swift 70 | import GooglePlacesAutocomplete // Not required when including source files directly in project 71 | 72 | let gpaViewController = GooglePlacesAutocomplete( 73 | apiKey: "[YOUR GOOGLE PLACES API KEY]", 74 | placeType: .Address 75 | ) 76 | 77 | gpaViewController.placeDelegate = self // Conforms to GooglePlacesAutocompleteDelegate 78 | 79 | presentViewController(gpaViewController, animated: true, completion: nil) 80 | ``` 81 | 82 | `GooglePlacesAutocompleteDelegate` supports three methods: 83 | 84 | - `placesFound(places: [Place])`: Invoked whenever the Google Places API is called 85 | - `placeSelected(place: Place)`: Invoked when a new place is selected 86 | - `placeViewClosed()`: Invoked when the view is closed 87 | 88 | Here's a [complete example](https://github.com/watsonbox/ios_google_places_autocomplete/blob/master/GooglePlacesAutocompleteExample/GooglePlacesAutocompleteExample/ViewController.swift). 89 | 90 | ### Place Details 91 | 92 | From Google's documentation: "you can request more details about a particular establishment or point of interest by initiating a [Place Details](https://developers.google.com/places/webservice/details) request. A Place Details request returns more comprehensive information about the indicated place such as its complete address, phone number, user rating and reviews." 93 | 94 | ```swift 95 | place.getDetails { details in 96 | println(details.name) // Convenience accessor for name 97 | println(details.latitude) // Convenience accessor for latitude 98 | println(details.longitude) // Convenience accessor for longitude 99 | println(details.raw) // Complete JSON data (see below) 100 | } 101 | 102 | /* 103 | [ 104 | status: OK, 105 | result: { 106 | "address_components" = ( 107 | { 108 | "long_name" = Paris; 109 | "short_name" = Paris; 110 | types = ( 111 | locality, 112 | political 113 | ); 114 | }, 115 | ... 116 | ); 117 | geometry = { 118 | location = { 119 | lat = "48.856614"; 120 | lng = "2.3522219"; 121 | }; 122 | ... 123 | */ 124 | ``` 125 | 126 | See the [documentation](https://developers.google.com/places/webservice/details#PlaceDetailsResponses) for full response details. 127 | 128 | ### Location Biasing 129 | 130 | The Place Autocomplete API supports biasing results to a specified circle by passing a `location` and a `radius` parameter. This instructs the service to *prefer* showing results within that circle. Results outside of the defined area may still be displayed. 131 | 132 | ```swift 133 | gpaViewController.locationBias = LocationBias(latitude: 48.8534275, longitude: 2.3582787999999937, radius: 1000) 134 | ``` 135 | 136 | 137 | ### Styling 138 | 139 | The UINavigationController appearance can also easily be changed, for example: 140 | 141 | ```swift 142 | gpaViewController.navigationBar.barStyle = UIBarStyle.Black 143 | gpaViewController.navigationBar.translucent = false 144 | gpaViewController.navigationBar.barTintColor = UIColor(red: 0.11, green: 0.27, blue: 0.53, alpha: 1.0) 145 | gpaViewController.navigationBar.tintColor = UIColor.whiteColor() 146 | gpaViewController.navigationBar.titleTextAttributes = [NSFontAttributeName: UIFont(name: "Zapfino", size: 16.0)!] 147 | ``` 148 | 149 | 150 | 151 | 152 | 153 |
154 | 155 | Also, to change the contents of the title bar: 156 | 157 | ```swift 158 | gpaViewController.navigationItem.title = "Enter City" 159 | gpaViewController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: gpaViewController, action: "close") 160 | ``` 161 | 162 | 163 | ## Contributing 164 | 165 | 1. Fork it ( https://github.com/watsonbox/ios-google-places-autocomplete/fork ) 166 | 2. Create your feature branch (`git checkout -b my-new-feature`) 167 | 3. Commit your changes (`git commit -am 'Add some feature'`) 168 | 4. Push to the branch (`git push origin my-new-feature`) 169 | 5. Create a new Pull Request 170 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'xcjobs' 2 | 3 | def destinations 4 | [ 'name=iPhone 6s,OS=9.0' ] 5 | end 6 | 7 | XCJobs::Test.new('test') do |t| 8 | t.workspace = 'GooglePlacesAutocomplete' 9 | t.scheme = 'GooglePlacesAutocompleteExample' 10 | t.configuration = 'Release' 11 | t.build_dir = 'build' 12 | t.formatter = 'xcpretty -c' 13 | 14 | destinations.each do |destination| 15 | t.add_destination(destination) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /Screenshots/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/watsonbox/ios_google_places_autocomplete/3b24a37eca923c5d92253122f26a2cd41f10ec5a/Screenshots/search.png -------------------------------------------------------------------------------- /Screenshots/style.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/watsonbox/ios_google_places_autocomplete/3b24a37eca923c5d92253122f26a2cd41f10ec5a/Screenshots/style.png -------------------------------------------------------------------------------- /Screenshots/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/watsonbox/ios_google_places_autocomplete/3b24a37eca923c5d92253122f26a2cd41f10ec5a/Screenshots/view.png --------------------------------------------------------------------------------