E2 on iOS

iOS apps call the E2 hosted REST endpoints with async/await and render the results as MapKit annotations, polylines, and stats.

Documentation navigation

How E2 fits

E2 is a plain HTTPS API, so it needs no iOS framework. Search returns GeoJSON that converts to MKPointAnnotation objects, routes become MKPolyline, and compute results drive your UI. MapKit renders all of it.

Setup

E2Client.swift
struct E2Client {
  // Configure the base URL for your deployment
  var baseURL = URL(string: "https://api.e2.example")!
  var session = URLSession.shared

  func get(_ path: String, query: [String: String]) async throws -> Data {
    var components = URLComponents(url: baseURL.appendingPathComponent(path),
                                    resolvingAgainstBaseURL: false)!
    components.queryItems = query.map { URLQueryItem(name: $0.key, value: $0.value) }
    let (data, _) = try await session.data(from: components.url!)
    return data
  }
}

Search

SearchViewModel.swift
struct FeatureCollection: Decodable {
  let features: [GeoFeature]
}
struct GeoFeature: Decodable {
  let geometry: Geometry
  let properties: [String: String?]?
}
struct Geometry: Decodable {
  let coordinates: [Double]
}

let data = try await client.get("search", query: [
  "feature": "restaurant", "area": "Manhattan", "limit": "500",
])
let places = try JSONDecoder().decode(FeatureCollection.self, from: data)

let annotations = places.features.map { feature -> MKPointAnnotation in
  let annotation = MKPointAnnotation()
  annotation.coordinate = CLLocationCoordinate2D(
    latitude: feature.geometry.coordinates[1],
    longitude: feature.geometry.coordinates[0],
  )
  annotation.title = feature.properties?["name"] ?? nil ?? "Restaurant"
  return annotation
}
mapView.addAnnotations(annotations)

Routing

RoutingViewModel.swift
struct RouteRequest: Encodable {
  let locations: [[String: Double]]
  let mode: String
}
struct RouteResponse: Decodable {
  let geometry: RouteGeometry
}
struct RouteGeometry: Decodable {
  let coordinates: [[Double]]
}

var request = URLRequest(url: baseURL.appendingPathComponent("route"))
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONEncoder().encode(RouteRequest(
  locations: [["lat": 40.74, "lon": -73.99], ["lat": 40.71, "lon": -74.01]],
  mode: "walking",
))

let (data, _) = try await session.data(for: request)
let route = try JSONDecoder().decode(RouteResponse.self, from: data)

let coords = route.geometry.coordinates.map {
  CLLocationCoordinate2D(latitude: $0[1], longitude: $0[0])
}
mapView.addOverlay(MKPolyline(coordinates: coords, count: coords.count))

Compute

ComputeViewModel.swift
struct CountResponse: Decodable {
  let count: Int
}

let data = try await client.get("compute/count", query: [
  "feature": "restaurant", "region": "Manhattan",
  "near": "park", "distance": "500",
])
let nearParks = try JSONDecoder().decode(CountResponse.self, from: data)

statusText = "\(nearParks.count) restaurants within 500 m of a park"

Notes

The base URL is configurable — point it at the E2 hosted platform or your own deployment. Implement rendererFor to style the route overlay, and store API keys in the keychain or an xcconfig file that stays out of source control.