diff --git a/Podfile b/Podfile index d5b7202..52d250f 100644 --- a/Podfile +++ b/Podfile @@ -2,4 +2,3 @@ platform :ios, '8.0' pod 'AFNetworking' pod 'BDBOAuth1Manager' - diff --git a/Podfile.lock b/Podfile.lock index 5698266..835b101 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,11 +1,11 @@ PODS: - AFNetworking (2.4.1): - - AFNetworking/NSURLConnection - - AFNetworking/NSURLSession - - AFNetworking/Reachability - - AFNetworking/Security - - AFNetworking/Serialization - - AFNetworking/UIKit + - AFNetworking/NSURLConnection (= 2.4.1) + - AFNetworking/NSURLSession (= 2.4.1) + - AFNetworking/Reachability (= 2.4.1) + - AFNetworking/Security (= 2.4.1) + - AFNetworking/Serialization (= 2.4.1) + - AFNetworking/UIKit (= 2.4.1) - AFNetworking/NSURLConnection (2.4.1): - AFNetworking/Reachability - AFNetworking/Security @@ -32,4 +32,4 @@ SPEC CHECKSUMS: AFNetworking: 0aabc6fae66d6e5d039eeb21c315843c7aae51ab BDBOAuth1Manager: 4f0967110286edd4c7961cf48a4eb242c64137a1 -COCOAPODS: 0.33.1 +COCOAPODS: 0.35.0 diff --git a/README.md b/README.md index b52a9e3..578c19d 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,45 @@ -### Basic Yelp client +## Yelp -This is a headless example of how to implement an OAuth 1.0a Yelp API client. The Yelp API provides an application token that allows applications to make unauthenticated requests to their search API. +This is a Yelp search app using the [Yelp API](http://developer.rottentomatoes.com/docs/read/JSON). -### Next steps +Time spent: 12 -- Check out `MainViewController.swift` to see how to use the `YelpClient`. -- Augment the search method in the `YelpClient` with whatever search parameters you want to support. +### Features -### Sample request +#### Required -``` -client = YelpClient(consumerKey: yelpConsumerKey, consumerSecret: yelpConsumerSecret, accessToken: yelpToken, accessSecret: yelpTokenSecret) +- [X] Search results page + - [X] Table rows should be dynamic height according to the content height + - [X] Custom cells should have the proper Auto Layout constraints + - [X] Search bar should be in the navigation bar (doesn't have to expand to show location like the real Yelp app does). (Please see notes.) +- [X] Filter page. Unfortunately, not all the filters are supported in the Yelp API. + - [X] The filters you should actually have are: category, sort (best match, distance, highest rated), radius (meters), deals (on/off). + - [X] The filters table should be organized into sections as in the mock. + - [X] You can use the default UISwitch for on/off states. Optional: implement a custom switch + - [X] Clicking on the "Search" button should dismiss the filters page and trigger the search w/ the new filter settings. + - [X] Display some of the available Yelp categories (choose any 3-4 that you want). (Please see notes.) -client.searchWithTerm("Thai", success: { (operation: AFHTTPRequestOperation!, response: AnyObject!) -> Void in - println(response) -}) { (operation: AFHTTPRequestOperation!, error: NSError!) -> Void in - println(error) -} +Notes: -``` +* The categories search field is implemented as a text field. The user enters a comma-separated list of category codes. +* I couldn't get the sort field to work. Selecting on a table cell does not select the cell. I think it has to do with the tap gesture that I created in that same view. +* I've added a search bar, but didn't get it to work. -### Sample response +#### Optional -``` -businesses = ( - { - categories = ( - ( - Thai, - thai - ) - ); - "display_phone" = "+1-415-931-6917"; - id = "lers-ros-thai-san-francisco"; - "image_url" = "http://s3-media2.ak.yelpcdn.com/bphoto/IStxUNVdfuPR2ddDAIPk_A/ms.jpg"; - "is_claimed" = 1; - "is_closed" = 0; - location = { - address = ( - "730 Larkin St" - ); - city = "San Francisco"; - "country_code" = US; - "cross_streets" = "Olive St & Ellis St"; - "display_address" = ( - "730 Larkin St", - "(b/t Olive St & Ellis St)", - Tenderloin, - "San Francisco, CA 94109" - ); - neighborhoods = ( - Tenderloin - ); - "postal_code" = 94109; - "state_code" = CA; - }; - "menu_date_updated" = 1387658025; - "menu_provider" = "single_platform"; - "mobile_url" = "http://m.yelp.com/biz/lers-ros-thai-san-francisco"; - name = "Lers Ros Thai"; - phone = 4159316917; - rating = 4; - "rating_img_url" = "http://s3-media4.ak.yelpcdn.com/assets/2/www/img/c2f3dd9799a5/ico/stars/v1/stars_4.png"; - "rating_img_url_large" = "http://s3-media2.ak.yelpcdn.com/assets/2/www/img/ccf2b76faa2c/ico/stars/v1/stars_large_4.png"; - "rating_img_url_small" = "http://s3-media4.ak.yelpcdn.com/assets/2/www/img/f62a5be2f902/ico/stars/v1/stars_small_4.png"; - "review_count" = 1154; - "snippet_image_url" = "http://s3-media4.ak.yelpcdn.com/photo/D40HpcJt-O6Ll654S_--6w/ms.jpg"; - "snippet_text" = "Fantastic pad-see-ew. Super rich, flavorful sauce and plenty of ginormous prawns, especially for a $12 price tag in San Francisco. I went through a pretty..."; - url = "http://www.yelp.com/biz/lers-ros-thai-san-francisco"; - } - ); - region = { - center = { - latitude = "37.7703124"; - longitude = "-122.43647245575"; - }; - span = { - "latitude_delta" = "0.06424638000000016"; - "longitude_delta" = "0.07145348265001417"; - }; - }; - total = 760; -``` +- [ ] Search results page + - [ ] Infinite scroll for restaurant results + - [ ] Implement map view of restaurant results +- [ ] Filter page + - [ ] Radius filter should expand as in the real Yelp app + - [ ] Categories should show a subset of the full list with a "See All" row to expand. Category list is here: http://www.yelp.com/developers/documentation/category_list (Links to an external site.) +- [ ] Implement the restaurant detail page. +### Walkthrough +![Video Walkthrough](yelp_demo.gif) + +Credits +--------- +* [Yelp API](http://www.yelp.com/developers/documentation) +* [AFNetworking](https://github.com/AFNetworking/AFNetworking) +* [LiceCap](http://www.cockos.com/licecap/) diff --git a/Yelp.xcodeproj/project.pbxproj b/Yelp.xcodeproj/project.pbxproj index c1cd275..47dba36 100644 --- a/Yelp.xcodeproj/project.pbxproj +++ b/Yelp.xcodeproj/project.pbxproj @@ -8,6 +8,11 @@ /* Begin PBXBuildFile section */ 03D1A025B413415787AD3DF3 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C56EF0CE844A4EE5819C72AF /* libPods.a */; }; + 1848E35F1A917BA1003A1402 /* FiltersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1848E35D1A917BA1003A1402 /* FiltersViewController.swift */; }; + 1848E3621A91A889003A1402 /* SearchOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1848E3611A91A889003A1402 /* SearchOptions.swift */; }; + 1861678A1A8F158300E9ADBA /* Business.swift in Sources */ = {isa = PBXBuildFile; fileRef = 186167891A8F158300E9ADBA /* Business.swift */; }; + 1861678E1A8F1D5C00E9ADBA /* BusinessCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1861678C1A8F1D5C00E9ADBA /* BusinessCell.swift */; }; + 1861678F1A8F1D5C00E9ADBA /* BusinessCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1861678D1A8F1D5C00E9ADBA /* BusinessCell.xib */; }; 222D8A5B19CCF9F900D2DB53 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222D8A5A19CCF9F900D2DB53 /* AppDelegate.swift */; }; 222D8A5D19CCF9F900D2DB53 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222D8A5C19CCF9F900D2DB53 /* ViewController.swift */; }; 222D8A6019CCF9F900D2DB53 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 222D8A5E19CCF9F900D2DB53 /* Main.storyboard */; }; @@ -28,6 +33,11 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 1848E35D1A917BA1003A1402 /* FiltersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FiltersViewController.swift; sourceTree = ""; }; + 1848E3611A91A889003A1402 /* SearchOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchOptions.swift; sourceTree = ""; }; + 186167891A8F158300E9ADBA /* Business.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Business.swift; sourceTree = ""; }; + 1861678C1A8F1D5C00E9ADBA /* BusinessCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BusinessCell.swift; sourceTree = ""; }; + 1861678D1A8F1D5C00E9ADBA /* BusinessCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BusinessCell.xib; sourceTree = ""; }; 222D8A5519CCF9F900D2DB53 /* Yelp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Yelp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 222D8A5919CCF9F900D2DB53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 222D8A5A19CCF9F900D2DB53 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -41,7 +51,8 @@ 222D8A7C19CCFAE000D2DB53 /* YelpClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YelpClient.swift; sourceTree = ""; }; 222D8A7E19CCFAEF00D2DB53 /* Yelp-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Yelp-Bridging-Header.h"; sourceTree = ""; }; C56EF0CE844A4EE5819C72AF /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D4E76156B42F474DB4157657 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = ""; }; + D5732CD97E4DF7DC274FB59C /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + E00CFE4A3AA1AD81E1F85AD8 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -63,14 +74,31 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 186167881A8F154700E9ADBA /* Models */ = { + isa = PBXGroup; + children = ( + 186167891A8F158300E9ADBA /* Business.swift */, + ); + name = Models; + sourceTree = ""; + }; + 1861678B1A8F1D1B00E9ADBA /* Views */ = { + isa = PBXGroup; + children = ( + 1861678C1A8F1D5C00E9ADBA /* BusinessCell.swift */, + 1861678D1A8F1D5C00E9ADBA /* BusinessCell.xib */, + ); + name = Views; + sourceTree = ""; + }; 222D8A4C19CCF9F900D2DB53 = { isa = PBXGroup; children = ( 222D8A5719CCF9F900D2DB53 /* Yelp */, 222D8A6D19CCF9F900D2DB53 /* YelpTests */, 222D8A5619CCF9F900D2DB53 /* Products */, - D4E76156B42F474DB4157657 /* Pods.xcconfig */, 4A7CA25ACD374CA0AC966A24 /* Frameworks */, + F745A2A08ABC6842A9E4C8D2 /* Pods */, ); sourceTree = ""; }; @@ -86,6 +114,8 @@ 222D8A5719CCF9F900D2DB53 /* Yelp */ = { isa = PBXGroup; children = ( + 1861678B1A8F1D1B00E9ADBA /* Views */, + 186167881A8F154700E9ADBA /* Models */, 222D8A7A19CCFAAA00D2DB53 /* API */, 222D8A7B19CCFAB600D2DB53 /* View Controllers */, 222D8A5A19CCF9F900D2DB53 /* AppDelegate.swift */, @@ -127,6 +157,7 @@ isa = PBXGroup; children = ( 222D8A7C19CCFAE000D2DB53 /* YelpClient.swift */, + 1848E3611A91A889003A1402 /* SearchOptions.swift */, ); name = API; sourceTree = ""; @@ -135,6 +166,7 @@ isa = PBXGroup; children = ( 222D8A5C19CCF9F900D2DB53 /* ViewController.swift */, + 1848E35D1A917BA1003A1402 /* FiltersViewController.swift */, ); name = "View Controllers"; sourceTree = ""; @@ -147,6 +179,15 @@ name = Frameworks; sourceTree = ""; }; + F745A2A08ABC6842A9E4C8D2 /* Pods */ = { + isa = PBXGroup; + children = ( + E00CFE4A3AA1AD81E1F85AD8 /* Pods.debug.xcconfig */, + D5732CD97E4DF7DC274FB59C /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -230,6 +271,7 @@ buildActionMask = 2147483647; files = ( 222D8A6019CCF9F900D2DB53 /* Main.storyboard in Resources */, + 1861678F1A8F1D5C00E9ADBA /* BusinessCell.xib in Resources */, 222D8A6519CCF9F900D2DB53 /* LaunchScreen.xib in Resources */, 222D8A6219CCF9F900D2DB53 /* Images.xcassets in Resources */, ); @@ -257,7 +299,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Pods-resources.sh\"\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; showEnvVarsInLog = 0; }; 4817EA46FAD24F44BB24E6FB /* Check Pods Manifest.lock */ = { @@ -282,8 +324,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1861678E1A8F1D5C00E9ADBA /* BusinessCell.swift in Sources */, 222D8A5D19CCF9F900D2DB53 /* ViewController.swift in Sources */, + 1861678A1A8F158300E9ADBA /* Business.swift in Sources */, 222D8A7D19CCFAE000D2DB53 /* YelpClient.swift in Sources */, + 1848E35F1A917BA1003A1402 /* FiltersViewController.swift in Sources */, + 1848E3621A91A889003A1402 /* SearchOptions.swift in Sources */, 222D8A5B19CCF9F900D2DB53 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -405,7 +451,7 @@ }; 222D8A7519CCF9F900D2DB53 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D4E76156B42F474DB4157657 /* Pods.xcconfig */; + baseConfigurationReference = E00CFE4A3AA1AD81E1F85AD8 /* Pods.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -419,7 +465,7 @@ }; 222D8A7619CCF9F900D2DB53 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D4E76156B42F474DB4157657 /* Pods.xcconfig */; + baseConfigurationReference = D5732CD97E4DF7DC274FB59C /* Pods.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/Yelp/Base.lproj/Main.storyboard b/Yelp/Base.lproj/Main.storyboard index a8ec515..0590d51 100644 --- a/Yelp/Base.lproj/Main.storyboard +++ b/Yelp/Base.lproj/Main.storyboard @@ -1,7 +1,8 @@ - + - + + @@ -15,11 +16,254 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yelp/Business.swift b/Yelp/Business.swift new file mode 100644 index 0000000..21a1f7e --- /dev/null +++ b/Yelp/Business.swift @@ -0,0 +1,49 @@ +// +// Business.swift +// Yelp +// +// Created by Joseph Ku on 2/13/15. +// Copyright (c) 2015 Timothy Lee. All rights reserved. +// + +import UIKit + +class Business: NSObject { + let METER_TO_MILES = 0.000621371 + + var name: String! + var imageUrl: String! + var ratingImageUrl: String! + var numReviews: Int! + var address: String! + var categories: [String]! + var distance: Double! + + init(dictionary: NSDictionary) { + println(dictionary) + + self.name = dictionary["name"] as String + self.imageUrl = dictionary["image_url"] as String + self.ratingImageUrl = dictionary["rating_img_url"] as String + self.numReviews = dictionary["review_count"] as Int + let location = dictionary["location"] as NSDictionary + let displayAddress = location["display_address"] as [String] + self.address = "\(displayAddress[0]), \(displayAddress[1])" + let categoriesArray: [[String]] = dictionary["categories"] as [[String]] + self.categories = categoriesArray.map({$0[0]}) + let meters = dictionary["distance"] as Double + self.distance = meters * METER_TO_MILES + } + + class func businessesWithDictionaries(dictionaries: [NSDictionary]) -> [Business] { + var businesses = [Business]() + + for dictionary in dictionaries { + var business = Business(dictionary: dictionary) + + businesses.append(business) + } + + return businesses + } +} diff --git a/Yelp/BusinessCell.swift b/Yelp/BusinessCell.swift new file mode 100644 index 0000000..77a9d59 --- /dev/null +++ b/Yelp/BusinessCell.swift @@ -0,0 +1,48 @@ +// +// BusinessCell.swift +// Yelp +// +// Created by Joseph Ku on 2/13/15. +// Copyright (c) 2015 Timothy Lee. All rights reserved. +// + +import UIKit + +class BusinessCell: UITableViewCell { + @IBOutlet weak var thumbImageView: UIImageView! + @IBOutlet weak var nameLabel: UILabel! + @IBOutlet weak var distanceLabel: UILabel! + @IBOutlet weak var ratingImageView: UIImageView! + @IBOutlet weak var reviewsLabel: UILabel! + @IBOutlet weak var addressLabel: UILabel! + @IBOutlet weak var categoriesLabel: UILabel! + + var business: Business! { + get { + return self.business + } + set(business) { + self.thumbImageView.setImageWithURL(NSURL(string: business.imageUrl)) + self.nameLabel.text = business.name + self.distanceLabel.text = NSString(format: "%0.2f mi", business.distance) + self.ratingImageView.setImageWithURL(NSURL(string: business.ratingImageUrl)) + self.reviewsLabel.text = NSString(format: "%d Reviews", business.numReviews) + self.addressLabel.text = business.address + self.categoriesLabel.text = ", ".join(business.categories) + } + } + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + self.thumbImageView.layer.cornerRadius = 3 + self.thumbImageView.clipsToBounds = true + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + +} diff --git a/Yelp/BusinessCell.xib b/Yelp/BusinessCell.xib new file mode 100644 index 0000000..124e69c --- /dev/null +++ b/Yelp/BusinessCell.xib @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Yelp/FiltersViewController.swift b/Yelp/FiltersViewController.swift new file mode 100644 index 0000000..a4f82be --- /dev/null +++ b/Yelp/FiltersViewController.swift @@ -0,0 +1,62 @@ +// +// FiltersViewController.swift +// Yelp +// +// Created by Joseph Ku on 2/15/15. +// Copyright (c) 2015 Timothy Lee. All rights reserved. +// + +import UIKit + + +class FiltersViewController: UITableViewController { + @IBOutlet weak var categoriesField: UITextField! + @IBOutlet weak var dealsSwitch: UISwitch! + @IBOutlet weak var radiusField: UITextField! + + let pickerValueArray = ["Best Matched", "Distance", "Highest Rated"] + var filterOptions: SearchOptions! + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + let tapRecognizer = UITapGestureRecognizer(target: self, action: "handleSingleTap:") + tapRecognizer.numberOfTapsRequired = 1 + self.view.addGestureRecognizer(tapRecognizer) + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + func handleSingleTap(recognizer: UITapGestureRecognizer) { + self.view.endEditing(true) + } + + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + // Get the new view controller using segue.destinationViewController. + // Pass the selected object to the new view controller. + + if segue.identifier == "ApplyFilters" { + let categories = categoriesField.text.componentsSeparatedByString(",") +// I couldn't get the row selection to work. The row returned is always nil. +// let row = self.tableView.indexPathForSelectedRow() +// println(row!.section) +// println(row!.row) + let radius = radiusField.text.toInt() + let includeDeals = dealsSwitch.on + + println(categories) + println(radius) + println(includeDeals) + + filterOptions = SearchOptions(categories: categories, sort: 1, radius: radius, includeDeals: includeDeals) + } + } +} diff --git a/Yelp/SearchOptions.swift b/Yelp/SearchOptions.swift new file mode 100644 index 0000000..124379c --- /dev/null +++ b/Yelp/SearchOptions.swift @@ -0,0 +1,23 @@ +// +// SearchOptions.swift +// Yelp +// +// Created by Joseph Ku on 2/15/15. +// Copyright (c) 2015 Timothy Lee. All rights reserved. +// + +import Foundation + +class SearchOptions { + var categories: [String]? + var sort: Int? + var radius: Int? + var includeDeals: Bool? + + init(categories: [String]?, sort: Int?, radius: Int?, includeDeals: Bool?) { + self.categories = categories + self.sort = sort + self.radius = radius + self.includeDeals = includeDeals + } +} diff --git a/Yelp/ViewController.swift b/Yelp/ViewController.swift index b89a9ab..99a0e1c 100644 --- a/Yelp/ViewController.swift +++ b/Yelp/ViewController.swift @@ -8,14 +8,17 @@ import UIKit -class ViewController: UIViewController { +class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + @IBOutlet weak var tableView: UITableView! + var client: YelpClient! + var businesses = [Business]() // You can register for Yelp API keys here: http://www.yelp.com/developers/manage_api_keys - let yelpConsumerKey = "vxKwwcR_NMQ7WaEiQBK_CA" - let yelpConsumerSecret = "33QCvh5bIF5jIHR5klQr7RtBDhQ" - let yelpToken = "uRcRswHFYa1VkDrGV6LAW2F8clGh5JHV" - let yelpTokenSecret = "mqtKIxMIR4iBtBPZCmCLEb-Dz3Y" + let yelpConsumerKey = "EA0Ahz2sN-eLIik1qYi45A" + let yelpConsumerSecret = "Y3R6PTlIJQJB9zU2RnIUB_OBqHQ" + let yelpToken = "Jf-rvyKh3m4sKjs_yUK3W7Yc6J6dJl1c" + let yelpTokenSecret = "yLbnl56G9pS691QXKeNg802EXP8" required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) @@ -23,14 +26,19 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. - client = YelpClient(consumerKey: yelpConsumerKey, consumerSecret: yelpConsumerSecret, accessToken: yelpToken, accessSecret: yelpTokenSecret) - client.searchWithTerm("Thai", success: { (operation: AFHTTPRequestOperation!, response: AnyObject!) -> Void in - println(response) - }) { (operation: AFHTTPRequestOperation!, error: NSError!) -> Void in - println(error) - } + tableView.delegate = self + tableView.dataSource = self + + tableView.registerNib(UINib(nibName: "BusinessCell", bundle:nil), forCellReuseIdentifier: "BusinessCell") + + tableView.rowHeight = UITableViewAutomaticDimension + + self.navigationItem.titleView = UISearchBar() + + performYelpSearch("Thai", options: nil) } override func didReceiveMemoryWarning() { @@ -38,6 +46,44 @@ class ViewController: UIViewController { // Dispose of any resources that can be recreated. } + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return businesses.count + } -} + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("BusinessCell") as BusinessCell + cell.business = businesses[indexPath.row] + + return cell + } + + @IBAction func cancelToViewController(segue:UIStoryboardSegue) { + } + + @IBAction func applyFilters(segue:UIStoryboardSegue) { + let controller = segue.sourceViewController as FiltersViewController + let filterOptions = controller.filterOptions + + println(filterOptions) + + performYelpSearch("Thai", options: filterOptions) + + dismissViewControllerAnimated(true, completion: nil) + } + + private func performYelpSearch(term: String, options: SearchOptions!) { + client = YelpClient(consumerKey: yelpConsumerKey, consumerSecret: yelpConsumerSecret, accessToken: yelpToken, accessSecret: yelpTokenSecret) + client.searchWithTerm(term, options: options, success: { (operation: AFHTTPRequestOperation!, response: AnyObject!) -> Void in + //println(response) + let dictionaries = response["businesses"] as [NSDictionary] + + self.businesses = Business.businessesWithDictionaries(dictionaries) + + self.tableView.reloadData() + + }) { (operation: AFHTTPRequestOperation!, error: NSError!) -> Void in + println(error) + } + } +} diff --git a/Yelp/Yelp-Bridging-Header.h b/Yelp/Yelp-Bridging-Header.h index 326fabf..d0a220e 100644 --- a/Yelp/Yelp-Bridging-Header.h +++ b/Yelp/Yelp-Bridging-Header.h @@ -4,3 +4,4 @@ #import "NSDictionary+BDBOAuth1Manager.h" #import "BDBOAuth1RequestOperationManager.h" +#import "UIImageView+AFNetworking.h" diff --git a/Yelp/YelpClient.swift b/Yelp/YelpClient.swift index a368467..dfd4b2f 100644 --- a/Yelp/YelpClient.swift +++ b/Yelp/YelpClient.swift @@ -26,12 +26,24 @@ class YelpClient: BDBOAuth1RequestOperationManager { self.requestSerializer.saveAccessToken(token) } - func searchWithTerm(term: String, success: (AFHTTPRequestOperation!, AnyObject!) -> Void, failure: (AFHTTPRequestOperation!, NSError!) -> Void) -> AFHTTPRequestOperation! { + func searchWithTerm(term: String, options: SearchOptions!, success: (AFHTTPRequestOperation!, AnyObject!) -> Void, failure: (AFHTTPRequestOperation!, NSError!) -> Void) -> AFHTTPRequestOperation! { // For additional parameters, see http://www.yelp.com/developers/documentation/v2/search_api - var parameters = ["term": term, "location": "San Francisco"] + var parameters = ["term": term, "ll": "37.78842,-122.40403"] + if options != nil { + if options.sort != nil { + parameters["sort"] = String(options.sort!) + } + if options.categories != nil || !options.categories!.isEmpty { + parameters["category_filter"] = ",".join(options.categories!) + } + if options.radius != nil { + parameters["radius_filter"] = String(options.radius!) + } + if options.includeDeals != nil { + parameters["deals_filter"] = options.includeDeals! ? "true" : "false" + } + } return self.GET("search", parameters: parameters, success: success, failure: failure) } } - - diff --git a/yelp_demo.gif b/yelp_demo.gif new file mode 100644 index 0000000..628565a Binary files /dev/null and b/yelp_demo.gif differ