优化首页位置上报体验
This commit is contained in:
@ -20,12 +20,12 @@ final class LocationProvider: NSObject, LocationProviding {
|
|||||||
permissionManager.desiredAccuracy = kCLLocationAccuracyBest
|
permissionManager.desiredAccuracy = kCLLocationAccuracyBest
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestSnapshot() async throws -> HomeLocationSnapshot {
|
func requestSnapshot(desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest) async throws -> HomeLocationSnapshot {
|
||||||
try AMapBootstrap.requireConfigured()
|
try AMapBootstrap.requireConfigured()
|
||||||
try await ensureLocationPermission()
|
try await ensureLocationPermission()
|
||||||
|
|
||||||
let manager = makeLocationManager(
|
let manager = makeLocationManager(
|
||||||
desiredAccuracy: kCLLocationAccuracyBest,
|
desiredAccuracy: desiredAccuracy,
|
||||||
locatingWithReGeocode: true
|
locatingWithReGeocode: true
|
||||||
)
|
)
|
||||||
return try await withCheckedThrowingContinuation { continuation in
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
|
|||||||
@ -15,8 +15,8 @@ struct HomeLocationSnapshot: Sendable, Equatable {
|
|||||||
|
|
||||||
/// 定位服务协议,便于 ViewModel 注入与单元测试 mock。
|
/// 定位服务协议,便于 ViewModel 注入与单元测试 mock。
|
||||||
protocol LocationProviding: Sendable {
|
protocol LocationProviding: Sendable {
|
||||||
/// 请求当前坐标与地址。
|
/// 按指定精度请求当前坐标与地址。
|
||||||
func requestSnapshot() async throws -> HomeLocationSnapshot
|
func requestSnapshot(desiredAccuracy: CLLocationAccuracy) async throws -> HomeLocationSnapshot
|
||||||
/// 按指定精度请求当前坐标。
|
/// 按指定精度请求当前坐标。
|
||||||
func requestCoordinate(desiredAccuracy: CLLocationAccuracy) async throws -> CLLocationCoordinate2D
|
func requestCoordinate(desiredAccuracy: CLLocationAccuracy) async throws -> CLLocationCoordinate2D
|
||||||
/// 逆地理编码。
|
/// 逆地理编码。
|
||||||
@ -24,6 +24,11 @@ protocol LocationProviding: Sendable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension LocationProviding {
|
extension LocationProviding {
|
||||||
|
/// 请求当前坐标与地址,默认使用最高精度以保持既有业务行为。
|
||||||
|
func requestSnapshot() async throws -> HomeLocationSnapshot {
|
||||||
|
try await requestSnapshot(desiredAccuracy: kCLLocationAccuracyBest)
|
||||||
|
}
|
||||||
|
|
||||||
/// 请求当前坐标,默认使用最高精度以保持既有业务行为。
|
/// 请求当前坐标,默认使用最高精度以保持既有业务行为。
|
||||||
func requestCoordinate() async throws -> CLLocationCoordinate2D {
|
func requestCoordinate() async throws -> CLLocationCoordinate2D {
|
||||||
try await requestCoordinate(desiredAccuracy: kCLLocationAccuracyBest)
|
try await requestCoordinate(desiredAccuracy: kCLLocationAccuracyBest)
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
// suixinkan
|
// suixinkan
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import CoreLocation
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// 位置上报成功结果。
|
/// 位置上报成功结果。
|
||||||
@ -16,6 +17,7 @@ struct LocationReportSuccessResult: Equatable {
|
|||||||
final class LocationReportService {
|
final class LocationReportService {
|
||||||
|
|
||||||
static let operationIntervalMillis: Int64 = 30_000
|
static let operationIntervalMillis: Int64 = 30_000
|
||||||
|
static let manualReportDesiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyHundredMeters
|
||||||
|
|
||||||
private(set) var lastOnlineStatusSwitchTime: Int64 = 0
|
private(set) var lastOnlineStatusSwitchTime: Int64 = 0
|
||||||
private(set) var lastLocationReportTime: Int64 = 0
|
private(set) var lastLocationReportTime: Int64 = 0
|
||||||
@ -87,7 +89,8 @@ final class LocationReportService {
|
|||||||
longitude: nil,
|
longitude: nil,
|
||||||
address: nil,
|
address: nil,
|
||||||
type: 1,
|
type: 1,
|
||||||
showSuccessToast: true
|
showSuccessToast: true,
|
||||||
|
desiredAccuracy: Self.manualReportDesiredAccuracy
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +101,8 @@ final class LocationReportService {
|
|||||||
longitude: Double?,
|
longitude: Double?,
|
||||||
address: String?,
|
address: String?,
|
||||||
type: Int,
|
type: Int,
|
||||||
showSuccessToast: Bool = true
|
showSuccessToast: Bool = true,
|
||||||
|
desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest
|
||||||
) async throws -> LocationReportSuccessResult {
|
) async throws -> LocationReportSuccessResult {
|
||||||
if type != 3 {
|
if type != 3 {
|
||||||
let now = currentTimestampMillis()
|
let now = currentTimestampMillis()
|
||||||
@ -136,7 +140,7 @@ final class LocationReportService {
|
|||||||
address: resolvedAddress
|
address: resolvedAddress
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
snapshot = try await locationProvider.requestSnapshot()
|
snapshot = try await locationProvider.requestSnapshot(desiredAccuracy: desiredAccuracy)
|
||||||
}
|
}
|
||||||
|
|
||||||
let locationText = snapshot.address.isEmpty
|
let locationText = snapshot.address.isEmpty
|
||||||
|
|||||||
@ -183,7 +183,11 @@ final class HomeViewController: BaseViewController {
|
|||||||
) as! HomeLocationReportCell
|
) as! HomeLocationReportCell
|
||||||
cell.cardView.onReportTap = { [weak self] in
|
cell.cardView.onReportTap = { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
Task { await self.viewModel.manualReportLocation(api: self.homeAPI) }
|
Task { @MainActor in
|
||||||
|
self.showLoading()
|
||||||
|
defer { self.hideLoading() }
|
||||||
|
await self.viewModel.manualReportLocation(api: self.homeAPI)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cell.apply(
|
cell.apply(
|
||||||
address: HomeViewModel.locationReportPromptText,
|
address: HomeViewModel.locationReportPromptText,
|
||||||
|
|||||||
@ -67,6 +67,27 @@ final class LocationReportServiceTests: XCTestCase {
|
|||||||
XCTAssertTrue(stateStore.isOnline)
|
XCTAssertTrue(stateStore.isOnline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testManualReportUsesLowerLocationAccuracy() async throws {
|
||||||
|
let reportJSON = """
|
||||||
|
{"code":100000,"msg":"success","data":{"staff_id":"staff-1","expired":0,"status":1}}
|
||||||
|
""".data(using: .utf8)!
|
||||||
|
let api = HomeAPI(client: APIClient(environment: .testing, session: MockURLSession(responses: [reportJSON])))
|
||||||
|
let stateStore = HomeLocationStateStore(store: appStore)
|
||||||
|
let locationProvider = MockLocationProvider()
|
||||||
|
let service = LocationReportService(
|
||||||
|
appStore: appStore,
|
||||||
|
locationStateStore: stateStore,
|
||||||
|
locationProvider: locationProvider
|
||||||
|
)
|
||||||
|
|
||||||
|
_ = try await service.manualReport(api: api)
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
locationProvider.requestedSnapshotAccuracies,
|
||||||
|
[LocationReportService.manualReportDesiredAccuracy]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func testTooFrequentManualReportThrows() async throws {
|
func testTooFrequentManualReportThrows() async throws {
|
||||||
let reportJSON = """
|
let reportJSON = """
|
||||||
{"code":100000,"msg":"success","data":{"staff_id":"staff-1","expired":0,"status":1}}
|
{"code":100000,"msg":"success","data":{"staff_id":"staff-1","expired":0,"status":1}}
|
||||||
|
|||||||
@ -13,9 +13,11 @@ final class MockLocationProvider: LocationProviding, @unchecked Sendable {
|
|||||||
var reverseGeocodeResult = "北京市东城区"
|
var reverseGeocodeResult = "北京市东城区"
|
||||||
var requestSnapshotError: Error?
|
var requestSnapshotError: Error?
|
||||||
var requestCoordinateError: Error?
|
var requestCoordinateError: Error?
|
||||||
|
var requestedSnapshotAccuracies: [CLLocationAccuracy] = []
|
||||||
var requestedAccuracies: [CLLocationAccuracy] = []
|
var requestedAccuracies: [CLLocationAccuracy] = []
|
||||||
|
|
||||||
func requestSnapshot() async throws -> HomeLocationSnapshot {
|
func requestSnapshot(desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest) async throws -> HomeLocationSnapshot {
|
||||||
|
requestedSnapshotAccuracies.append(desiredAccuracy)
|
||||||
if let requestSnapshotError { throw requestSnapshotError }
|
if let requestSnapshotError { throw requestSnapshotError }
|
||||||
return snapshot
|
return snapshot
|
||||||
}
|
}
|
||||||
|
|||||||
@ -245,7 +245,7 @@ private final class MockPunchPointLocationProvider: LocationProviding, @unchecke
|
|||||||
self.reverseAddress = reverseAddress
|
self.reverseAddress = reverseAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestSnapshot() async throws -> HomeLocationSnapshot {
|
func requestSnapshot(desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest) async throws -> HomeLocationSnapshot {
|
||||||
snapshot
|
snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1465,7 +1465,7 @@ private final class MockRiskMapLocationProvider: LocationProviding, @unchecked S
|
|||||||
self.onCoordinateRequest = onCoordinateRequest
|
self.onCoordinateRequest = onCoordinateRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestSnapshot() async throws -> HomeLocationSnapshot {
|
func requestSnapshot(desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest) async throws -> HomeLocationSnapshot {
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let latitude, let longitude):
|
case .success(let latitude, let longitude):
|
||||||
return HomeLocationSnapshot(latitude: latitude, longitude: longitude, address: "测试地址")
|
return HomeLocationSnapshot(latitude: latitude, longitude: longitude, address: "测试地址")
|
||||||
|
|||||||
Reference in New Issue
Block a user