问题描述
这个问题困扰着我好几个月了,我相信这归结于我使用错误的结构和程序。
我正在尝试对Yelp的API进行API调用,并将变量传递给用户的经/纬度。我可以根据当前的LocationManager来获取经度/纬度,但是当纬度/经度似乎只有在调用API之后才可用,因此API会为经度/纬度获取默认的0.0值
在这方面,我非常初级,但是有一种方法可以设置一个加载屏幕,该屏幕可以在后台捕获经纬度,并在我的ExploreView显示时,显示实际位置信息已经建立了吗?
下面是我的LocationManager和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)
}
}
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()
}
}
解决方法
尝试以下修改后的代码(我需要进行一些复制,因此请注意-可能出现一些错字)。
主要思想是订阅LocationManager更新的位置发布者,以侦听位置的显式更改,并仅在位置真正更新后才执行下一次API加载,而不会为空。
struct ExploreView: View {
@ObservedObject var location: LocationManager
@ObservedObject var fetcher: RestaurantFetcher
init() {
let location = LocationManager() // << use only one instance
self.location = location
self.fetcher = RestaurantFetcher(locationManager: location)
self.location.startUpdating() // << do this only once
}
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()
}
}
}
import Combine
public class RestaurantFetcher: ObservableObject {
@Published var businesses = [RestaurantResponse]()
private var locationManager: LocationManager
var lat: String {
return "\(locationManager.location?.coordinate.latitude ?? 0.0)"
}
var long: String {
return "\(locationManager.location?.coordinate.longitude ?? 0.0)"
}
private var subscriber: AnyCancellable?
init(locationManager: LocationManager) {
self.locationManager = locationManager
// listen for available location explicitly
subscriber = locationManager.$location
.debounce(for: 5,scheduler: DispatchQueue.main) // wait for 5 sec to avoid often reload
.receive(on: DispatchQueue.main)
.sink { [weak self] location in
guard location != nil else { return }
self?.load()
}
}
func load() {
print("\(locationManager.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.locationManager.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()
}
}