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) -> 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() } }