Christopher Hotchkiss Christopher Hotchkiss
Login

Code

remindwall / RWPureSwift / Sources / TrafficAPI / PlaceSearch.swift

Back to remindwall — Current RemindWall

Download this file

import CoreLocation
import Dependencies
import DependenciesMacros
import Foundation
import MapKit

/// One MKLocalSearch hit as plain values, for destination and home-origin
/// picking in the alerts settings.
public struct FoundPlace: Equatable, Identifiable, Sendable {
    public let id: String
    public let name: String
    public let address: String
    public let latitude: Double
    public let longitude: Double

    public init(id: String, name: String, address: String, latitude: Double, longitude: Double) {
        self.id = id
        self.name = name
        self.address = address
        self.latitude = latitude
        self.longitude = longitude
    }
}

@DependencyClient
public struct PlaceSearchClient: Sendable {
    /// Natural-language place lookup via Apple Maps.
    public var search: @Sendable (_ query: String) async throws -> [FoundPlace]
}

extension PlaceSearchClient: DependencyKey {
    public static var liveValue: Self {
        Self(
            search: { query in
                let request = MKLocalSearch.Request()
                request.naturalLanguageQuery = query
                let response = try await MKLocalSearch(request: request).start()
                return response.mapItems.compactMap { item in
                    guard let name = item.name else { return nil }
                    let coordinate = item.placemark.coordinate
                    return FoundPlace(
                        id: "\(name)|\(coordinate.latitude)|\(coordinate.longitude)",
                        name: name,
                        address: item.placemark.title ?? "",
                        latitude: coordinate.latitude,
                        longitude: coordinate.longitude
                    )
                }
            }
        )
    }
}

extension PlaceSearchClient: TestDependencyKey {
    public static let testValue = Self()

    public static var previewValue: Self {
        Self(
            search: { query in
                [
                    FoundPlace(
                        id: "preview-1", name: "Maple Elementary",
                        address: "402 School Rd, Seattle",
                        latitude: 47.5423, longitude: -122.3866
                    ),
                    FoundPlace(
                        id: "preview-2", name: "\(query) Learning Ctr",
                        address: "98 Pine Ave, Renton",
                        latitude: 47.48, longitude: -122.2
                    ),
                ]
            }
        )
    }
}

extension DependencyValues {
    public var placeSearch: PlaceSearchClient {
        get { self[PlaceSearchClient.self] }
        set { self[PlaceSearchClient.self] = newValue }
    }
}