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
|