SwiftUI - 获取用户坐标以传递 API 调用
Posted
技术标签:
【中文标题】SwiftUI - 获取用户坐标以传递 API 调用【英文标题】:SwiftUI - Get User's coordinates to pass in API call 【发布时间】:2020-08-26 20:09:50 【问题描述】:这个问题已经困扰我好几个月了,我相信这归结为我使用了错误的结构和程序。
我正在尝试对 Yelp 的 API 进行 API 调用,并为用户的纬度/经度传递变量。我能够根据我当前的 LocationManager 获取纬度/经度,但是当似乎纬度/经度似乎仅在 API 调用之后才可用时,因此 API 获取纬度/经度的默认值 0.0 .
我是一个初学者成立了吗?
下面是我的 LocationManager 和 ExploreView
位置管理器
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(启动时显示的第一个视图)
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()
【问题讨论】:
你有没有设法解决它? 【参考方案1】:尝试以下修改后的代码(我需要进行一些复制,所以请注意 - 可能有一些拼写错误)。
主要思想是订阅 LocationManager 更新的位置发布者,以侦听位置的显式变化,并仅在位置真正更新而不是 nil 后执行下一次 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()
【讨论】:
不错,这正是我想要的,它看起来符合 SwiftUI 2.0。有没有机会使用内置的属性包装器来实现类似的结果?以上是关于SwiftUI - 获取用户坐标以传递 API 调用的主要内容,如果未能解决你的问题,请参考以下文章