Files
maps-saas/packages/ios-swift-sdk/Sources/IntaleqMaps/Services/IntaleqGeocodingService.swift
T

153 lines
6.0 KiB
Swift

import Foundation
public struct InlqGeocodingPlace: Codable {
public let id: String
public let name: String
public let nameAr: String?
public let nameEn: String?
public let category: String?
public let city: String?
public let neighbourhood: String?
public let address: String?
public let position: InlqLatLng
}
/**
* Commercial Geocoding & Reverse Geocoding Service calling Intaleq SaaS API on iOS (Swift).
*/
public class IntaleqGeocodingService {
public static let shared = IntaleqGeocodingService()
private init() {}
/**
* Search for places, addresses, and POIs by text query.
*/
public func search(
query: String,
proximity: InlqLatLng? = nil,
limit: Int = 10,
completion: @escaping (Result<[InlqGeocodingPlace], Error>) -> Void
) {
guard let encoded = query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
completion(.failure(NSError(domain: "IntaleqMaps", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid query"])))
return
}
var urlStr = "\(IntaleqMaps.shared.baseUrl)/api/geocoding/search?q=\(encoded)&limit=\(limit)"
if let proximity = proximity {
urlStr += "&lat=\(proximity.latitude)&lng=\(proximity.longitude)"
}
guard let url = URL(string: urlStr) else {
completion(.failure(NSError(domain: "IntaleqMaps", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue(IntaleqMaps.shared.apiKey, forHTTPHeaderField: "x-api-key")
request.setValue("application/json", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: request) { data, _, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.success([]))
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let features = json["features"] as? [[String: Any]] {
let places: [InlqGeocodingPlace] = features.compactMap { f in
guard let geom = f["geometry"] as? [String: Any],
let coords = geom["coordinates"] as? [Double],
coords.count >= 2 else { return nil }
let props = f["properties"] as? [String: Any] ?? [:]
return InlqGeocodingPlace(
id: f["id"] as? String ?? UUID().uuidString,
name: props["name"] as? String ?? "موقع محدد",
nameAr: props["name_ar"] as? String,
nameEn: props["name_en"] as? String,
category: props["category"] as? String,
city: props["city"] as? String,
neighbourhood: props["neighbourhood"] as? String,
address: props["address"] as? String,
position: InlqLatLng(latitude: coords[1], longitude: coords[0])
)
}
completion(.success(places))
} else {
completion(.success([]))
}
} catch {
completion(.failure(error))
}
}.resume()
}
/**
* Reverse geocode coordinate to get street and neighborhood name.
*/
public func reverseGeocode(
position: InlqLatLng,
completion: @escaping (Result<InlqGeocodingPlace?, Error>) -> Void
) {
let urlStr = "\(IntaleqMaps.shared.baseUrl)/api/geocoding/reverse?lat=\(position.latitude)&lng=\(position.longitude)"
guard let url = URL(string: urlStr) else {
completion(.failure(NSError(domain: "IntaleqMaps", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])))
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue(IntaleqMaps.shared.apiKey, forHTTPHeaderField: "x-api-key")
request.setValue("application/json", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: request) { data, _, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.success(nil))
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let features = json["features"] as? [[String: Any]], let first = features.first,
let geom = first["geometry"] as? [String: Any],
let coords = geom["coordinates"] as? [Double], coords.count >= 2 {
let props = first["properties"] as? [String: Any] ?? [:]
let place = InlqGeocodingPlace(
id: first["id"] as? String ?? UUID().uuidString,
name: props["name"] as? String ?? "موقع محدد",
nameAr: props["name_ar"] as? String,
nameEn: props["name_en"] as? String,
category: props["category"] as? String,
city: props["city"] as? String,
neighbourhood: props["neighbourhood"] as? String,
address: props["address"] as? String,
position: InlqLatLng(latitude: coords[1], longitude: coords[0])
)
completion(.success(place))
} else {
completion(.success(nil))
}
} catch {
completion(.failure(error))
}
}.resume()
}
}