https://www.yelp.com/developers/v3/manage_app
https://github.com/YamamotoDesu/CoffeeQuest/blob/main/CoffeeQuest/Resources/APIKeys.swift
rm Podfile.lock
pod deintegrate && pod install
Under the Debug menu in the Simulator, Choose "Custom Location".
Set latitude and longitude.
location: Shinjuku
longitude: 139.710007
latitude: 35.685001
import UIKit
import MapKit
public class BusinessMapViewModel: NSObject {
// MARK: - Properties
public let coordinate: CLLocationCoordinate2D
public let name: String
public let rating: Double
public let image: UIImage
public let ratingDescription: String
// MARK: - Object Lifecycle
public init(coordinate: CLLocationCoordinate2D,
name: String,
rating: Double,
image: UIImage) {
self.coordinate = coordinate
self.name = name
self.rating = rating
self.image = image
self.ratingDescription = "\(rating) stars"
}
}
// MARK: - MKAnnotation
extension BusinessMapViewModel: MKAnnotation {
public var title: String? {
return name
}
public var subtitle: String? {
return ratingDescription
}
}
import MapKit
import YelpAPI
public class AnnotationFactory {
public func createBusinessMapViewModel(for business: Business) -> BusinessMapViewModel {
let coordinate = business.location
let name = business.name
let rating = business.rating
let image: UIImage
switch rating {
case 0.0..<3.0: image = UIImage(named: "terrible")!
case 3.0..<3.5: image = UIImage(named: "bad")!
case 3.5..<4.0: image = UIImage(named: "meh")!
case 4.0..<4.75: image = UIImage(named: "good")!
case 4.75...5.0: image = UIImage(named: "great")!
default: image = UIImage(named: "bad")!
}
return BusinessMapViewModel(coordinate: coordinate,
name: name,
rating: rating,
image: image)
}
}
public protocol BusinessSearchClient {
func search(with coordinate: CLLocationCoordinate2D,
term: String,
limit: UInt,
offset: UInt,
success: @escaping (([Business]) -> Void),
failure: @escaping ((Error?) -> Void))
}
import MapKit
public struct Business {
var name: String
var rating: Double
var location: CLLocationCoordinate2D
}
YLPClient+2BBusinessSerachClient
import MapKit
import YelpAPI
extension YLPClient: BusinessSearchClient {
public func search(with coordinate: CLLocationCoordinate2D,
term: String,
limit: UInt,
offset: UInt,
success: @escaping (([Business]) -> Void),
failure: @escaping ((Error?) -> Void)) {
let yelpCoordinate = YLPCoordinate(latitude: coordinate.latitude,
longitude: coordinate.longitude)
search(with: yelpCoordinate,
term: term,
limit: limit,
offset: offset,
sort: .bestMatched) { (searchResult, error) in
guard let searchResult = searchResult, error == nil else {
failure(error)
return
}
let businesses = searchResult.businesses.adaptToBusinesses()
success(businesses)
}
}
}
extension Array where Element: YLPBusiness {
func adaptToBusinesses() -> [Business] {
return compactMap { yelpBusiness in
guard let yelpCoordinate = yelpBusiness.location.coordinate else {
return nil
}
let coordinate = CLLocationCoordinate2D(latitude: yelpCoordinate.latitude,
longitude: yelpCoordinate.longitude)
return Business(name: yelpBusiness.name,
rating: yelpBusiness.rating,
location: coordinate)
}
}
}
import MapKit
import YelpAPI
public class ViewController: UIViewController {
// MARK: - Properties
public let annotationFactory = AnnotationFactory()
private var businesses: [Business] = []
public var client: BusinessSearchClient = YLPClient(apiKey: YelpAPIKey)
private let locationManager = CLLocationManager()
// MARK: - Outlets
@IBOutlet public var mapView: MKMapView! {
didSet {
mapView.showsUserLocation = true
}
}
// MARK: - View Lifecycle
public override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.async { [weak self] in
self?.locationManager.requestWhenInUseAuthorization()
}
}
// MARK: - Actions
@IBAction func businessFilterToggleChanged(_ sender: UISwitch) {
}
}
// MARK: - MKMapViewDelegate
extension ViewController: MKMapViewDelegate {
public func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
centerMap(on: userLocation.coordinate)
}
private func centerMap(on coordinate: CLLocationCoordinate2D) {
let regionRadius: CLLocationDistance = 3000
let coordinateRegion = MKCoordinateRegion(center: coordinate,
latitudinalMeters: regionRadius,
longitudinalMeters: regionRadius)
mapView.setRegion(coordinateRegion, animated: true)
searchForBusinesses()
}
private func searchForBusinesses() {
client.search(with: mapView.userLocation.coordinate,
term: "coffee",
limit: 35,
offset: 0,
success: { [weak self] (businesses) in
guard let self = self else { return }
self.businesses = businesses
DispatchQueue.main.async {
self.addAnnotations()
}
}, failure: { error in
print("Search failed: \(String(describing: error))")
})
}
private func addAnnotations() {
for business in businesses {
let viewModel = annotationFactory.createBusinessMapViewModel(for: business)
mapView.addAnnotation(viewModel)
}
}
public func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let viewModel = annotation as? BusinessMapViewModel else { return nil }
let identifier = "business"
let annotationView: MKAnnotationView
if let existingView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) {
annotationView = existingView
} else {
annotationView = MKAnnotationView(annotation: viewModel, reuseIdentifier: identifier)
}
annotationView.image = viewModel.image
annotationView.canShowCallout = true
return annotationView
}
}