105 lines
3.2 KiB
Swift
105 lines
3.2 KiB
Swift
//
|
|
// LocationGeocoder.swift
|
|
// suixinkan
|
|
//
|
|
|
|
import CoreLocation
|
|
import Foundation
|
|
|
|
/// 地址逆地理编码能力,供平台定位实现注入。
|
|
@MainActor
|
|
protocol LocationAddressGeocoding: AnyObject {
|
|
/// 将坐标解析为可读地址,失败时返回空字符串。
|
|
func reverseGeocode(latitude: Double, longitude: Double) async -> String
|
|
}
|
|
|
|
/// 平台逆地理编码封装;真机使用高德,模拟器使用 Core Location。
|
|
@MainActor
|
|
final class LocationGeocoder: NSObject, LocationAddressGeocoding {
|
|
|
|
nonisolated static let shared = LocationGeocoder()
|
|
|
|
#if targetEnvironment(simulator)
|
|
private let geocoder = CLGeocoder()
|
|
#else
|
|
private var searchAPI: AMapSearchAPI?
|
|
private var continuation: CheckedContinuation<String, Never>?
|
|
#endif
|
|
|
|
nonisolated private override init() {
|
|
super.init()
|
|
}
|
|
|
|
/// 将坐标解析为可读地址。
|
|
func reverseGeocode(latitude: Double, longitude: Double) async -> String {
|
|
#if targetEnvironment(simulator)
|
|
let location = CLLocation(latitude: latitude, longitude: longitude)
|
|
guard let placemark = try? await geocoder.reverseGeocodeLocation(location).first else {
|
|
return ""
|
|
}
|
|
if let name = placemark.name?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
!name.isEmpty {
|
|
return name
|
|
}
|
|
return [
|
|
placemark.administrativeArea,
|
|
placemark.locality,
|
|
placemark.subLocality,
|
|
placemark.thoroughfare,
|
|
placemark.subThoroughfare,
|
|
]
|
|
.compactMap { $0?.trimmingCharacters(in: .whitespacesAndNewlines) }
|
|
.filter { !$0.isEmpty }
|
|
.joined()
|
|
#else
|
|
do {
|
|
try AMapBootstrap.requireConfigured()
|
|
} catch {
|
|
return ""
|
|
}
|
|
|
|
return await withCheckedContinuation { continuation in
|
|
self.continuation?.resume(returning: "")
|
|
self.continuation = continuation
|
|
|
|
guard let api = makeSearchAPI() else {
|
|
continuation.resume(returning: "")
|
|
return
|
|
}
|
|
let request = AMapReGeocodeSearchRequest()
|
|
request.location = AMapGeoPoint.location(withLatitude: CGFloat(latitude), longitude: CGFloat(longitude))
|
|
request.requireExtension = true
|
|
request.radius = 200
|
|
api.aMapReGoecodeSearch(request)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !targetEnvironment(simulator)
|
|
func onReGeocodeSearchDone(_ request: AMapReGeocodeSearchRequest!, response: AMapReGeocodeSearchResponse!) {
|
|
let address = response?.regeocode?.formattedAddress ?? ""
|
|
continuation?.resume(returning: address)
|
|
continuation = nil
|
|
}
|
|
|
|
func aMapSearchRequest(_ request: Any!, didFailWithError error: Error!) {
|
|
continuation?.resume(returning: "")
|
|
continuation = nil
|
|
}
|
|
|
|
private func makeSearchAPI() -> AMapSearchAPI? {
|
|
if let searchAPI {
|
|
return searchAPI
|
|
}
|
|
guard let api = AMapSearchAPI() else { return nil }
|
|
api.delegate = self
|
|
searchAPI = api
|
|
return api
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !targetEnvironment(simulator)
|
|
extension LocationGeocoder: AMapSearchDelegate {}
|
|
#endif
|