SPB Git

spb/focale Public

Swift 100%
2.2 KB · 77 lines swift
Raw Blame History
1//2//  PlaceProvider.swift3//  Focale4//5//  Author: Simon-Pierre Boucher6//  Contact: contact@spboucher.ai7//8//  Turns the device location into a coarse PlaceRef (locality, never raw9//  coordinates — CLAUDE.md §4). Everything stays on-device; CLGeocoder10//  lookups are throttled and cached.11//1213import CoreLocation14import Foundation15import Observation1617@MainActor18@Observable19final class PlaceProvider: NSObject, CLLocationManagerDelegate {2021    private(set) var currentPlace: PlaceRef?2223    private let manager = CLLocationManager()24    private let geocoder = CLGeocoder()25    private var lastGeocodedLocation: CLLocation?2627    override init() {28        super.init()29        manager.delegate = self30        manager.desiredAccuracy = kCLLocationAccuracyHundredMeters31    }3233    func startIfAuthorized() {34        switch manager.authorizationStatus {35        case .notDetermined:36            manager.requestWhenInUseAuthorization()37        case .authorizedWhenInUse, .authorizedAlways:38            manager.startUpdatingLocation()39        default:40            break41        }42    }4344    func stop() {45        manager.stopUpdatingLocation()46    }4748    nonisolated func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {49        Task { @MainActor in self.startIfAuthorized() }50    }5152    nonisolated func locationManager(53        _ manager: CLLocationManager,54        didUpdateLocations locations: [CLLocation]55    ) {56        guard let location = locations.last else { return }57        Task { @MainActor in self.geocodeIfNeeded(location) }58    }5960    private func geocodeIfNeeded(_ location: CLLocation) {61        // Re-geocode only after moving ~500 m; place names don't change faster.62        if let previous = lastGeocodedLocation,63           location.distance(from: previous) < 500 { return }64        lastGeocodedLocation = location6566        Task {67            guard let placemark = try? await geocoder.reverseGeocodeLocation(location).first68            else { return }69            currentPlace = PlaceRef(70                name: placemark.areasOfInterest?.first,71                locality: placemark.locality,72                countryCode: placemark.isoCountryCode73            )74        }75    }76}77