我为此苦苦挣扎了很长时间,却没有找到我错的地方(我知道我错了)。
我有一个带有电话位置的 API 调用(这个正在工作),但我想要通过文本字段输入手动位置的相同 API 调用(使用地理编码来检索纬度/经度)。地理编码部分正常且已更新,但未在 API 调用中传递。
我还希望在使用电话位置的专用按钮清除 TextField 时触发此 API 调用。
请问,我错过了什么?谢谢你的帮助。
更新:这适用于 Xcode 12.2 beta 2,应该适用于 Xcode 12.0.1
这是代码:
我的模特
import Foundation
struct MyModel: Codable {
let value: Double
}
我的视图模型
import Foundation
import SwiftUI
import Combine
final class MyViewModel: ObservableObject {
@Published var state = State.ready
@Published var value: MyModel = MyModel(value: 0.0)
@Published var manualLocation: String {
didSet {
UserDefaults.standard.set(manualLocation, forKey: "manualLocation")
}
}
@EnvironmentObject var coordinates: Coordinates
init() {
manualLocation = UserDefaults.standard.string(forKey: "manualLocation") ?? ""
}
enum State {
case ready
case loading(Cancellable)
case loaded
case error(Error)
}
private var url: URL {
get {
return URL(string: "https://myapi.com&lat=\(coordinates.latitude)&lon=\(coordinates.longitude)")!
}
}
let urlSession = URLSession.shared
var dataTask: AnyPublisher<MyModel, Error> {
self.urlSession
.dataTaskPublisher(for: self.url)
.map { $0.data }
.decode(type: MyModel.self, decoder: JSONDecoder())
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
func load(){
assert(Thread.isMainThread)
self.state = .loading(self.dataTask.sink(
receiveCompletion: { completion in
switch completion {
case .finished:
print("⚠️ API Call finished")
break
case let .failure(error):
print("❌ API Call failure")
self.state = .error(error)
}
},
receiveValue: { value in
self.state = .loaded
self.value = value
print(" API Call loaded")
}
))
}
}
位置管理器
import Foundation
import SwiftUI
import Combine
import CoreLocation
import MapKit
final class Coordinates: NSObject, ObservableObject {
@EnvironmentObject var myViewModel: MyViewModel
@Published var latitude: Double = 0.0
@Published var longitude: Double = 0.0
@Published var placemark: CLPlacemark? {
willSet { objectWillChange.send() }
}
private let locationManager = CLLocationManager()
private let geocoder = CLGeocoder()
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
deinit {
locationManager.stopUpdatingLocation()
}
}
extension Coordinates: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
latitude = location.coordinate.latitude
longitude = location.coordinate.longitude
geocoder.reverseGeocodeLocation(location, completionHandler: { (places, error) in
self.placemark = places?[0]
})
self.locationManager.stopUpdatingLocation()
}
}
extension Coordinates {
func getLocation(from address: String, completion: @escaping (_ location: CLLocationCoordinate2D?)-> Void) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
guard let placemarks = placemarks,
let location = placemarks.first?.location?.coordinate else {
completion(nil)
return
}
completion(location)
}
}
}
风景
import Foundation
import SwiftUI
struct MyView: View {
@EnvironmentObject var myViewModel: MyViewModel
@EnvironmentObject var coordinates: Coordinates
private var icon: Image { return Image(systemName: "location.fill") }
var body: some View {
VStack{
VStack{
Text("\(icon) \(coordinates.placemark?.locality ?? "Unknown location")")
Text("Latitude: \(coordinates.latitude)")
Text("Longitude: \(coordinates.longitude)")
}
VStack{
Text("UV Index: \(myViewModel.value.value)")
.disableAutocorrection(true)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
HStack{
TextField("Manual location", text: $myViewModel.manualLocation)
if !myViewModel.manualLocation.isEmpty{
Button(action: { clear() }) { Image(systemName: "xmark.circle.fill").foregroundColor(.gray) }
}
}
}.padding()
}
func commit() {
coordinates.getLocation(from: self.myViewModel.manualLocation) { places in
coordinates.latitude = places?.latitude ?? 0.0
coordinates.longitude = places?.longitude ?? 0.0
}
myViewModel.load()
}
func clear() {
myViewModel.manualLocation = ""
myViewModel.load()
}
}