This problem has been haunting me for months and I believe it comes down to my using the wrong structure and procedure.
I'm trying to do an API call to Yelp's API and passing in the variables for the user's lat/long. I'm able to grab the lat/long based on my current LocationManager, however when it seems as though the lat/long only becomes available AFTER the API call has been made, so the API is getting default 0.0 values for both lat/long.
I'm very much a beginner when it comes to this, but is there a way that I could set up a loading screen that grabs the lat/long in the background and by the time my ExploreView shows, the real location information has been established?
Below is my LocationManager and ExploreView
LocationManager
import Foundation
import CoreLocation
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
let geoCoder = CLGeocoder()
@Published var location: CLLocation? = nil
@Published var placemark: CLPlacemark? = nil
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.distanceFilter = kCLDistanceFilterNone
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func geoCode(with location: CLLocation) {
geoCoder.reverseGeocodeLocation(location) { (placemark, error) in
if error != nil {
print(error!.localizedDescription)
} else {
self.placemark = placemark?.first
}
}
}
func startUpdating() {
self.locationManager.delegate = self
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else {
return
}
self.location = location
self.geoCode(with: location)
}
}
ExploreView (The first view that shows upon launch)
import SwiftUI
import CoreLocation
import Foundation
struct ExploreView: View {
@ObservedObject var location = LocationManager()
@ObservedObject var fetcher: RestaurantFetcher
init() {
let location = LocationManager()
self.location = location
self.fetcher = RestaurantFetcher(locationManager: location)
self.location.startUpdating()
}
var body: some View {
ScrollView (.vertical) {
VStack {
HStack {
Text("Discover ")
.font(.system(size: 28))
.fontWeight(.bold)
+ Text(" \(location.placemark?.locality ?? "")")
.font(.system(size: 28))
.fontWeight(.bold)
Spacer()
}
HStack {
SearchBar(text: .constant(""))
}.padding(.top, 16)
HStack {
Text("Featured Restaurants")
.font(.system(size: 24))
.fontWeight(.bold)
Spacer()
NavigationLink(
destination: FeaturedView(),
label: {
Text("View All")
})
}.padding(.vertical, 30)
HStack {
Text("All Cuisines")
.font(.system(size: 24))
.fontWeight(.bold)
Spacer()
}
Spacer()
}.padding()
}
}
}
public class RestaurantFetcher: ObservableObject {
@Published var businesses = [RestaurantResponse]()
@ObservedObject var locationManager: LocationManager
let location = LocationManager()
var lat: String {
return "\(location.location?.coordinate.latitude ?? 0.0)"
}
var long: String {
return "\(location.location?.coordinate.longitude ?? 0.0)"
}
init(locationManager: LocationManager) {
let location = LocationManager()
self.locationManager = location
self.location.startUpdating()
load()
}
func load() {
print("\(location.location?.coordinate.latitude ?? 0.0)")
print("user latitude top of function")
//Returns default values of 0.0
let apikey = "APIKEY Here"
let url = URL(string: "https://api.yelp.com/v3/businesses/search?latitude=\(lat)&longitude=\(long)&radius=40000")!
var request = URLRequest(url: url)
request.setValue("Bearer \(apikey)", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
if let d = data {
print("\(self.location.location?.coordinate.longitude ?? 0.0)")
let decodedLists = try JSONDecoder().decode(BusinessesResponse.self, from: d)
// Returns actual location coordinates
DispatchQueue.main.async {
self.businesses = decodedLists.restaurants
}
} else {
print("No Data")
}
} catch {
print ("Caught")
}
}.resume()
}
}