19 changed files with 976 additions and 779 deletions
@ -1,38 +0,0 @@ |
|||||
.idea/ |
|
||||
.vagrant/ |
|
||||
.sconsign.dblite |
|
||||
.svn/ |
|
||||
|
|
||||
.DS_Store |
|
||||
*.swp |
|
||||
profile |
|
||||
|
|
||||
DerivedData/ |
|
||||
build/ |
|
||||
GeneratedPluginRegistrant.h |
|
||||
GeneratedPluginRegistrant.m |
|
||||
|
|
||||
.generated/ |
|
||||
|
|
||||
*.pbxuser |
|
||||
*.mode1v3 |
|
||||
*.mode2v3 |
|
||||
*.perspectivev3 |
|
||||
|
|
||||
!default.pbxuser |
|
||||
!default.mode1v3 |
|
||||
!default.mode2v3 |
|
||||
!default.perspectivev3 |
|
||||
|
|
||||
xcuserdata |
|
||||
|
|
||||
*.moved-aside |
|
||||
|
|
||||
*.pyc |
|
||||
*sync/ |
|
||||
Icon? |
|
||||
.tags* |
|
||||
|
|
||||
/Flutter/Generated.xcconfig |
|
||||
/Flutter/ephemeral/ |
|
||||
/Flutter/flutter_export_environment.sh |
|
||||
@ -1,131 +0,0 @@ |
|||||
import Foundation |
|
||||
import AMapLocationKit |
|
||||
|
|
||||
extension Notification.Name { |
|
||||
static let locationstartEvent = Notification.Name("locationstartEvent") //启动定位 |
|
||||
static let updatelocationEvent = Notification.Name("updatelocationEvent") //更新位置事件 |
|
||||
} |
|
||||
class LocationService: NSObject { |
|
||||
static let shared = LocationService() |
|
||||
private let TAG = "LocationService" |
|
||||
|
|
||||
private var locationManager: AMapLocationManager! |
|
||||
private var currentLocation: CLLocation? |
|
||||
private var currentReGeocode: AMapLocationReGeocode? |
|
||||
private var initialized = false |
|
||||
|
|
||||
private override init() { |
|
||||
super.init() |
|
||||
} |
|
||||
|
|
||||
// 初始化定位服务 |
|
||||
func initialize() { |
|
||||
if initialized { return } |
|
||||
print("\(TAG)--------- 初始化高德定位服务") |
|
||||
|
|
||||
locationManager = AMapLocationManager() |
|
||||
locationManager.delegate = self |
|
||||
|
|
||||
locationManager.desiredAccuracy = kCLLocationAccuracyBest // 定位精度 |
|
||||
locationManager.distanceFilter = 10.0 // 移动距离阈值(单位:米) |
|
||||
locationManager.locatingWithReGeocode = true // 是否返回逆地理编码信息 |
|
||||
locationManager.allowsBackgroundLocationUpdates = true // 是否允许后台定位 |
|
||||
locationManager.locationTimeout = 10 // 定位超时时间(秒) |
|
||||
locationManager.reGeocodeTimeout = 5 // 逆地理编码超时时间(秒) |
|
||||
|
|
||||
locationManager.startUpdatingLocation() |
|
||||
|
|
||||
initialized = true |
|
||||
} |
|
||||
|
|
||||
// 获取缓存的位置信息 |
|
||||
func getCachedLocation() -> [String: String]? { |
|
||||
guard let location = currentLocation else { return nil } |
|
||||
|
|
||||
var locationInfo: [String: String] = [ |
|
||||
"user_lat": "\(location.coordinate.latitude)", |
|
||||
"user_lng": "\(location.coordinate.longitude)" |
|
||||
] |
|
||||
|
|
||||
if let regeocode = currentReGeocode { |
|
||||
let address = [ |
|
||||
regeocode.province, // 省 |
|
||||
regeocode.city, // 市 |
|
||||
regeocode.district, // 区 |
|
||||
regeocode.street, // 街道 |
|
||||
regeocode.number // 门牌号 |
|
||||
].compactMap { $0 }.joined(separator: "") |
|
||||
|
|
||||
locationInfo["user_location"] = address |
|
||||
} |
|
||||
|
|
||||
return locationInfo |
|
||||
} |
|
||||
|
|
||||
// 获取当前位置(带回调) |
|
||||
func getCurrentLocation(completion: @escaping ([String: String]?, Error?) -> Void) { |
|
||||
if let cached = getCachedLocation() { |
|
||||
completion(cached, nil) |
|
||||
} else { |
|
||||
let error = NSError( |
|
||||
domain: "LocationService", |
|
||||
code: -1, |
|
||||
userInfo: [NSLocalizedDescriptionKey: "当前位置还未获取"] |
|
||||
) |
|
||||
completion(nil, error) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 停止定位服务 |
|
||||
func stop() { |
|
||||
locationManager.stopUpdatingLocation() |
|
||||
locationManager = nil |
|
||||
currentLocation = nil |
|
||||
currentReGeocode = nil |
|
||||
initialized = false |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// MARK: - AMapLocationManagerDelegate |
|
||||
extension LocationService: AMapLocationManagerDelegate { |
|
||||
// 定位更新回调 |
|
||||
func amapLocationManager(_ manager: AMapLocationManager!, didUpdate location: CLLocation!, reGeocode: AMapLocationReGeocode!) { |
|
||||
currentLocation = location |
|
||||
currentReGeocode = reGeocode |
|
||||
|
|
||||
print("\(TAG)--------- 定位更新: \(location.coordinate.latitude), \(location.coordinate.longitude)") |
|
||||
|
|
||||
var locationInfo: [String: String] = [ |
|
||||
"user_lat": "\(location.coordinate.latitude)", |
|
||||
"user_lng": "\(location.coordinate.longitude)" |
|
||||
] |
|
||||
if let regeocode = currentReGeocode { |
|
||||
let address = [ |
|
||||
reGeocode.province, // 省 |
|
||||
reGeocode.city, // 市 |
|
||||
reGeocode.district, // 区 |
|
||||
reGeocode.street, // 街道 |
|
||||
reGeocode.number // 门牌号 |
|
||||
].compactMap { $0 }.joined(separator: "") |
|
||||
print("\(TAG)--------- 定位地址: \(address)") |
|
||||
locationInfo["user_location"] = address |
|
||||
} |
|
||||
|
|
||||
|
|
||||
NotificationCenter.default.post( |
|
||||
name: .updatelocationEvent, |
|
||||
object: nil, |
|
||||
userInfo: locationInfo |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
// 定位失败回调 |
|
||||
func amapLocationManager(_ manager: AMapLocationManager!, didFailWithError error: Error!) { |
|
||||
print("\(TAG)--------- 定位失败: \(error.localizedDescription)") |
|
||||
} |
|
||||
|
|
||||
// 权限变更回调 |
|
||||
func amapLocationManager(_ manager: AMapLocationManager!, doRequireLocationAuth locationManager: CLLocationManager!) { |
|
||||
locationManager.requestWhenInUseAuthorization() |
|
||||
} |
|
||||
} |
|
||||
@ -1,44 +0,0 @@ |
|||||
import Flutter |
|
||||
import UIKit |
|
||||
import AMapLocationKit |
|
||||
|
|
||||
public class LocationServicePlugin: NSObject, FlutterPlugin { |
|
||||
private static var channel: FlutterMethodChannel? |
|
||||
|
|
||||
public static func register(with registrar: FlutterPluginRegistrar) { |
|
||||
let channel = FlutterMethodChannel(name: "com.yunqiinnovation.location_service", binaryMessenger: registrar.messenger()) |
|
||||
let instance = LocationServicePlugin() |
|
||||
// 必须先设置隐私合规 |
|
||||
DispatchQueue.main.async { |
|
||||
// 2. API Key |
|
||||
AMapServices.shared()?.apiKey = "aa60a03be9f6531bcc64029e60f95d03" |
|
||||
AMapServices.shared().enableHTTPS = true |
|
||||
// 3. 打印验证(调试用) |
|
||||
print("高德SDK初始化完成") |
|
||||
} |
|
||||
registrar.addMethodCallDelegate(instance, channel: channel) |
|
||||
self.channel = channel |
|
||||
instance.addstartlocationmonitor() |
|
||||
} |
|
||||
|
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
|
||||
switch call.method { |
|
||||
case "location": |
|
||||
result(nil) |
|
||||
default: |
|
||||
result(FlutterMethodNotImplemented) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private func addstartlocationmonitor(){ |
|
||||
// 通常放在启动后或模块初始化时 |
|
||||
NotificationCenter.default.addObserver( |
|
||||
forName: .locationstartEvent, |
|
||||
object: nil, |
|
||||
queue: .main |
|
||||
) { notification in |
|
||||
print("📥 收到 启动定位服务事件") |
|
||||
LocationService.shared.initialize() |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,27 +0,0 @@ |
|||||
# |
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. |
|
||||
# Run `pod lib lint location_service.podspec` to validate before publishing. |
|
||||
# |
|
||||
Pod::Spec.new do |s| |
|
||||
s.name = 'location_service' |
|
||||
s.version = '0.0.1' |
|
||||
s.summary = 'A new Flutter project.' |
|
||||
s.description = <<-DESC |
|
||||
A new Flutter project. |
|
||||
DESC |
|
||||
s.homepage = 'http://example.com' |
|
||||
s.license = { :file => '../LICENSE' } |
|
||||
s.author = { 'Your Company' => 'email@example.com' } |
|
||||
s.source = { :path => '.' } |
|
||||
s.source_files = 'Classes/**/*' |
|
||||
s.dependency 'Flutter' |
|
||||
s.dependency 'AMapLocation', '~> 2.10.0' |
|
||||
|
|
||||
s.static_framework = true |
|
||||
s.pod_target_xcconfig = { |
|
||||
'DEFINES_MODULE' => 'YES', |
|
||||
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' |
|
||||
} |
|
||||
s.platform = :ios, '12.0' |
|
||||
s.swift_version = '5.0' |
|
||||
end |
|
||||
@ -0,0 +1,21 @@ |
|||||
|
// swift-tools-version: 5.9 |
||||
|
import PackageDescription |
||||
|
|
||||
|
let package = Package( |
||||
|
name: "location_service", |
||||
|
platforms: [ |
||||
|
.iOS(.v16), |
||||
|
.macOS(.v13) |
||||
|
], |
||||
|
products: [ |
||||
|
.library(name: "location-service", targets: ["location_service"]) |
||||
|
], |
||||
|
dependencies: [ ], |
||||
|
targets: [ |
||||
|
.target( |
||||
|
name: "location_service", |
||||
|
dependencies: [], |
||||
|
path: "Sources/location_service" |
||||
|
) |
||||
|
] |
||||
|
) |
||||
@ -0,0 +1,7 @@ |
|||||
|
import Foundation |
||||
|
|
||||
|
/// 定位回调协议 |
||||
|
public protocol LocationCallback: AnyObject { |
||||
|
func onLocationSuccess(latitude: Double, longitude: Double, address: String) |
||||
|
func onLocationError(errorCode: Int, errorMessage: String) |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
import Foundation |
||||
|
|
||||
|
/// 位置数据结构 |
||||
|
public struct LocationData: Codable { |
||||
|
public let latitude: Double |
||||
|
public let longitude: Double |
||||
|
public let address: String |
||||
|
public let timestamp: TimeInterval |
||||
|
|
||||
|
public init(latitude: Double, longitude: Double, address: String, timestamp: TimeInterval) { |
||||
|
self.latitude = latitude |
||||
|
self.longitude = longitude |
||||
|
self.address = address |
||||
|
self.timestamp = timestamp |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,357 @@ |
|||||
|
import Foundation |
||||
|
import CoreLocation |
||||
|
import UIKit |
||||
|
|
||||
|
public extension Notification.Name { |
||||
|
static let locationStartEvent = Notification.Name("locationStartEvent") |
||||
|
static let updateLocationEvent = Notification.Name("updateLocationEvent") |
||||
|
} |
||||
|
|
||||
|
public class LocationService: NSObject { |
||||
|
private static let TAG = "LocationService" |
||||
|
private static let CACHE_PREFS_NAME = "location_cache" |
||||
|
private static let PERMISSION_REQUEST_CODE = 1001 |
||||
|
|
||||
|
public static let shared = LocationService() |
||||
|
|
||||
|
private var locationManager: CLLocationManager? |
||||
|
private var initialized = false |
||||
|
private var permissionRetryTimer: Timer? |
||||
|
|
||||
|
// 存储当前的定位请求回调(Flutter用) |
||||
|
private var currentLocationCallback: ((Result<[String: Any], Error>) -> Void)? |
||||
|
// 存储权限请求回调(Flutter用) |
||||
|
private var permissionRequestCallback: ((Result<[String: Bool], Error>) -> Void)? |
||||
|
|
||||
|
// 存储服务间调用的回调 |
||||
|
private var serviceCallbacks: [LocationCallback] = [] |
||||
|
|
||||
|
// 缓存的位置数据 |
||||
|
private var cachedLocationData: LocationData? |
||||
|
|
||||
|
private override init() { |
||||
|
super.init() |
||||
|
} |
||||
|
|
||||
|
/// 检查应用是否在前台运行 |
||||
|
public func isAppInForeground() -> Bool { |
||||
|
return UIApplication.shared.applicationState == .active |
||||
|
} |
||||
|
|
||||
|
/// 初始化定位服务 |
||||
|
public func initialize() { |
||||
|
if initialized { return } |
||||
|
|
||||
|
if !checkPermissions() { |
||||
|
print("\(LocationService.TAG): 没有定位权限,等待用户授权中...") |
||||
|
startPermissionMonitor() |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
setupLocationManager() |
||||
|
initialized = true |
||||
|
print("\(LocationService.TAG): 定位服务初始化完成") |
||||
|
} |
||||
|
|
||||
|
private func setupLocationManager() { |
||||
|
locationManager = CLLocationManager() |
||||
|
locationManager?.delegate = self |
||||
|
locationManager?.desiredAccuracy = kCLLocationAccuracyBest |
||||
|
locationManager?.distanceFilter = 10 |
||||
|
} |
||||
|
|
||||
|
private func startPermissionMonitor() { |
||||
|
permissionRetryTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in |
||||
|
guard let self = self else { return } |
||||
|
let hasPermission = self.checkPermissions() |
||||
|
if hasPermission && !self.initialized { |
||||
|
print("\(LocationService.TAG): 权限已授予,重新初始化定位服务") |
||||
|
self.initialize() |
||||
|
self.stopPermissionMonitor() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private func stopPermissionMonitor() { |
||||
|
permissionRetryTimer?.invalidate() |
||||
|
permissionRetryTimer = nil |
||||
|
} |
||||
|
|
||||
|
/// 检查定位权限 |
||||
|
private func checkPermissions() -> Bool { |
||||
|
return hasForegroundLocationPermission() |
||||
|
} |
||||
|
|
||||
|
private func hasForegroundLocationPermission() -> Bool { |
||||
|
let status = CLLocationManager.authorizationStatus() |
||||
|
return status == .authorizedWhenInUse || status == .authorizedAlways |
||||
|
} |
||||
|
|
||||
|
private func hasBackgroundLocationPermission() -> Bool { |
||||
|
return CLLocationManager.authorizationStatus() == .authorizedAlways |
||||
|
} |
||||
|
|
||||
|
/// 检查定位权限状态(Flutter用) |
||||
|
public func checkLocationPermission(completion: @escaping (Result<[String: Bool], Error>) -> Void) { |
||||
|
let hasPermission = checkPermissions() |
||||
|
let result: [String: Bool] = [ |
||||
|
"hasPermission": hasPermission, |
||||
|
"hasForegroundPermission": hasForegroundLocationPermission(), |
||||
|
"hasBackgroundPermission": hasBackgroundLocationPermission() |
||||
|
] |
||||
|
completion(.success(result)) |
||||
|
} |
||||
|
|
||||
|
/// 请求定位权限(Flutter用) |
||||
|
public func requestLocationPermission(completion: @escaping (Result<[String: Bool], Error>) -> Void) { |
||||
|
if checkPermissions() { |
||||
|
completion(.success(["granted": true])) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
permissionRequestCallback = completion |
||||
|
locationManager?.requestWhenInUseAuthorization() |
||||
|
} |
||||
|
|
||||
|
/// 按需获取当前位置(Flutter用) |
||||
|
public func getCurrentLocation(completion: @escaping (Result<[String: Any], Error>) -> Void) { |
||||
|
if !checkPermissions() { |
||||
|
completion(.failure(NSError(domain: "LocationService", code: -2, userInfo: [NSLocalizedDescriptionKey: "需要定位权限"]))) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if !initialized { |
||||
|
completion(.failure(NSError(domain: "LocationService", code: -3, userInfo: [NSLocalizedDescriptionKey: "定位服务未初始化"]))) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if currentLocationCallback != nil { |
||||
|
completion(.failure(NSError(domain: "LocationService", code: -4, userInfo: [NSLocalizedDescriptionKey: "正在定位中,请稍后再试"]))) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 保存回调,开始定位 |
||||
|
currentLocationCallback = completion |
||||
|
locationManager?.requestLocation() |
||||
|
} |
||||
|
|
||||
|
// MARK: - 供其他iOS服务使用的接口 |
||||
|
|
||||
|
/// 检查是否有定位权限(供其他服务使用) |
||||
|
public func hasLocationPermission() -> Bool { |
||||
|
return checkPermissions() |
||||
|
} |
||||
|
|
||||
|
/// 异步获取当前位置(供其他iOS服务使用) |
||||
|
public func getCurrentLocation() async throws -> LocationData { |
||||
|
return try await withCheckedThrowingContinuation { continuation in |
||||
|
getCurrentLocation { result in |
||||
|
switch result { |
||||
|
case .success(let locationInfo): |
||||
|
// 从返回的字典中提取位置信息 |
||||
|
if let latStr = locationInfo["user_lat"] as? String, |
||||
|
let lngStr = locationInfo["user_lng"] as? String, |
||||
|
let latitude = Double(latStr), |
||||
|
let longitude = Double(lngStr), |
||||
|
let address = locationInfo["user_location"] as? String { |
||||
|
|
||||
|
let locationData = LocationData( |
||||
|
latitude: latitude, |
||||
|
longitude: longitude, |
||||
|
address: address, |
||||
|
timestamp: Date().timeIntervalSince1970 * 1000 |
||||
|
) |
||||
|
continuation.resume(returning: locationData) |
||||
|
} else { |
||||
|
continuation.resume(throwing: NSError(domain: "LocationService", code: -5, userInfo: [NSLocalizedDescriptionKey: "位置数据格式错误"])) |
||||
|
} |
||||
|
case .failure(let error): |
||||
|
continuation.resume(throwing: error) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 获取当前位置(供其他iOS服务使用) |
||||
|
public func requestLocation(callback: LocationCallback) { |
||||
|
if !checkPermissions() { |
||||
|
callback.onLocationError(errorCode: -2, errorMessage: "没有定位权限") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
if !initialized { |
||||
|
callback.onLocationError(errorCode: -3, errorMessage: "定位服务未初始化") |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 添加到回调列表 |
||||
|
serviceCallbacks.append(callback) |
||||
|
|
||||
|
// 如果没有正在进行的定位请求,开始定位 |
||||
|
if currentLocationCallback == nil && serviceCallbacks.count == 1 { |
||||
|
locationManager?.requestLocation() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 同步获取缓存的位置信息(供其他服务使用) |
||||
|
/// 注意:这个方法返回的可能是过期的位置信息 |
||||
|
public func getCachedLocationData() -> LocationData? { |
||||
|
// 优先返回内存中的缓存 |
||||
|
if let cached = cachedLocationData { |
||||
|
return cached |
||||
|
} |
||||
|
|
||||
|
// 从UserDefaults中读取缓存 |
||||
|
return loadCachedLocationFromUserDefaults() |
||||
|
} |
||||
|
|
||||
|
private func saveCachedLocationToUserDefaults(_ locationData: LocationData) { |
||||
|
do { |
||||
|
let encoder = JSONEncoder() |
||||
|
let data = try encoder.encode(locationData) |
||||
|
UserDefaults.standard.set(data, forKey: "latitude") |
||||
|
UserDefaults.standard.set(String(locationData.latitude), forKey: "latitude") |
||||
|
UserDefaults.standard.set(String(locationData.longitude), forKey: "longitude") |
||||
|
UserDefaults.standard.set(locationData.address, forKey: "address") |
||||
|
UserDefaults.standard.set(Double(locationData.timestamp), forKey: "timestamp") |
||||
|
UserDefaults.standard.synchronize() |
||||
|
} catch { |
||||
|
print("\(LocationService.TAG): 保存位置缓存失败: \(error.localizedDescription)") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private func loadCachedLocationFromUserDefaults() -> LocationData? { |
||||
|
do { |
||||
|
guard let latitudeStr = UserDefaults.standard.string(forKey: "latitude"), |
||||
|
let longitudeStr = UserDefaults.standard.string(forKey: "longitude"), |
||||
|
let latitude = Double(latitudeStr), |
||||
|
let longitude = Double(longitudeStr) else { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
let address = UserDefaults.standard.string(forKey: "address") ?? "未知地址" |
||||
|
let timestampDouble = UserDefaults.standard.double(forKey: "timestamp") |
||||
|
|
||||
|
if timestampDouble == 0 { return nil } |
||||
|
|
||||
|
return LocationData( |
||||
|
latitude: latitude, |
||||
|
longitude: longitude, |
||||
|
address: address, |
||||
|
timestamp: TimeInterval(timestampDouble) |
||||
|
) |
||||
|
} catch { |
||||
|
print("\(LocationService.TAG): 读取位置缓存失败: \(error.localizedDescription)") |
||||
|
return nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// 停止定位服务 |
||||
|
public func stop() { |
||||
|
locationManager?.stopUpdatingLocation() |
||||
|
locationManager = nil |
||||
|
currentLocationCallback = nil |
||||
|
permissionRequestCallback = nil |
||||
|
serviceCallbacks.removeAll() |
||||
|
initialized = false |
||||
|
stopPermissionMonitor() |
||||
|
} |
||||
|
|
||||
|
private func handleLocationResult(_ location: CLLocation?, error: Error?) { |
||||
|
// 处理Flutter回调 |
||||
|
let flutterCallback = currentLocationCallback |
||||
|
currentLocationCallback = nil |
||||
|
|
||||
|
if let location = location, error == nil { |
||||
|
print("\(LocationService.TAG): 定位成功: \(location.coordinate.latitude), \(location.coordinate.longitude)") |
||||
|
|
||||
|
// 进行逆地理编码获取地址 |
||||
|
let geocoder = CLGeocoder() |
||||
|
geocoder.reverseGeocodeLocation(location) { [weak self] placemarks, geocodeError in |
||||
|
guard let self = self else { return } |
||||
|
|
||||
|
let address = placemarks?.first?.name ?? "未知地址" |
||||
|
|
||||
|
// 缓存位置数据 |
||||
|
let locationData = LocationData( |
||||
|
latitude: location.coordinate.latitude, |
||||
|
longitude: location.coordinate.longitude, |
||||
|
address: address, |
||||
|
timestamp: TimeInterval(Date().timeIntervalSince1970) * 1000 |
||||
|
) |
||||
|
self.cachedLocationData = locationData |
||||
|
self.saveCachedLocationToUserDefaults(locationData) |
||||
|
|
||||
|
// 通知Flutter |
||||
|
if let callback = flutterCallback { |
||||
|
let result: [String: Any] = [ |
||||
|
"user_location": address, |
||||
|
"user_lat": String(location.coordinate.latitude), |
||||
|
"user_lng": String(location.coordinate.longitude) |
||||
|
] |
||||
|
callback(.success(result)) |
||||
|
} |
||||
|
|
||||
|
// 通知其他iOS服务 |
||||
|
self.serviceCallbacks.forEach { callback in |
||||
|
callback.onLocationSuccess( |
||||
|
latitude: location.coordinate.latitude, |
||||
|
longitude: location.coordinate.longitude, |
||||
|
address: address |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
// 清空服务回调列表(单次定位) |
||||
|
self.serviceCallbacks.removeAll() |
||||
|
} |
||||
|
} else { |
||||
|
let errorMessage = error?.localizedDescription ?? "未知错误" |
||||
|
print("\(LocationService.TAG): 定位失败: \(errorMessage)") |
||||
|
|
||||
|
// 通知Flutter |
||||
|
if let callback = flutterCallback { |
||||
|
callback(.failure(NSError(domain: "LocationService", code: -1, userInfo: [NSLocalizedDescriptionKey: "定位失败: \(errorMessage)"]))) |
||||
|
} |
||||
|
|
||||
|
// 通知其他iOS服务 |
||||
|
serviceCallbacks.forEach { callback in |
||||
|
callback.onLocationError(errorCode: -1, errorMessage: errorMessage) |
||||
|
} |
||||
|
|
||||
|
// 清空服务回调列表 |
||||
|
serviceCallbacks.removeAll() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// MARK: - CLLocationManagerDelegate |
||||
|
extension LocationService: CLLocationManagerDelegate { |
||||
|
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { |
||||
|
guard let location = locations.last else { return } |
||||
|
handleLocationResult(location, error: nil) |
||||
|
} |
||||
|
|
||||
|
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { |
||||
|
handleLocationResult(nil, error: error) |
||||
|
} |
||||
|
|
||||
|
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { |
||||
|
print("\(LocationService.TAG): 定位权限状态变更: \(status.rawValue)") |
||||
|
|
||||
|
switch status { |
||||
|
case .authorizedWhenInUse, .authorizedAlways: |
||||
|
if !initialized { |
||||
|
initialize() |
||||
|
} |
||||
|
permissionRequestCallback?(.success(["granted": true])) |
||||
|
permissionRequestCallback = nil |
||||
|
case .denied, .restricted: |
||||
|
permissionRequestCallback?(.success(["granted": false])) |
||||
|
permissionRequestCallback = nil |
||||
|
case .notDetermined: |
||||
|
break |
||||
|
@unknown default: |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
import Foundation |
||||
|
|
||||
|
#if canImport(Flutter) |
||||
|
import Flutter |
||||
|
import UIKit |
||||
|
|
||||
|
public class LocationServicePlugin: NSObject, FlutterPlugin { |
||||
|
private static var channel: FlutterMethodChannel? |
||||
|
|
||||
|
public static func register(with registrar: FlutterPluginRegistrar) { |
||||
|
let channel = FlutterMethodChannel(name: "com.yunqiinnovation.location_service", binaryMessenger: registrar.messenger()) |
||||
|
let instance = LocationServicePlugin() |
||||
|
|
||||
|
registrar.addMethodCallDelegate(instance, channel: channel) |
||||
|
self.channel = channel |
||||
|
instance.addStartLocationMonitor() |
||||
|
} |
||||
|
|
||||
|
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
||||
|
switch call.method { |
||||
|
case "location": |
||||
|
result(nil) |
||||
|
case "initialize": |
||||
|
LocationService.shared.initialize() |
||||
|
result(nil) |
||||
|
case "getCurrentLocation": |
||||
|
LocationService.shared.getCurrentLocation { locationResult in |
||||
|
switch locationResult { |
||||
|
case .success(let locationInfo): |
||||
|
result(locationInfo) |
||||
|
case .failure(let error): |
||||
|
result(FlutterError(code: "LOCATION_ERROR", message: error.localizedDescription, details: nil)) |
||||
|
} |
||||
|
} |
||||
|
case "checkPermission": |
||||
|
LocationService.shared.checkLocationPermission { permissionResult in |
||||
|
switch permissionResult { |
||||
|
case .success(let permissions): |
||||
|
result(permissions) |
||||
|
case .failure(let error): |
||||
|
result(FlutterError(code: "PERMISSION_ERROR", message: error.localizedDescription, details: nil)) |
||||
|
} |
||||
|
} |
||||
|
case "requestPermission": |
||||
|
LocationService.shared.requestLocationPermission { permissionResult in |
||||
|
switch permissionResult { |
||||
|
case .success(let permissions): |
||||
|
result(permissions) |
||||
|
case .failure(let error): |
||||
|
result(FlutterError(code: "PERMISSION_ERROR", message: error.localizedDescription, details: nil)) |
||||
|
} |
||||
|
} |
||||
|
case "stop": |
||||
|
LocationService.shared.stop() |
||||
|
result(nil) |
||||
|
default: |
||||
|
result(FlutterMethodNotImplemented) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private func addStartLocationMonitor() { |
||||
|
NotificationCenter.default.addObserver( |
||||
|
forName: .locationStartEvent, |
||||
|
object: nil, |
||||
|
queue: .main |
||||
|
) { notification in |
||||
|
print("📥 收到启动定位服务事件") |
||||
|
LocationService.shared.initialize() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
Loading…
Reference in new issue