AppleMapsServer
Elixir client for the Apple Maps Server API.
Features
-
ES256 JWT signing and
/v1/tokenaccess-token exchange - Supervised token cache (no per-call token minting)
- Place search, autocomplete, forward/reverse geocoding, directions, ETAs
-
Optional response decoding into typed structs (
Place,Coordinate) - Typed errors with status-aware messages
Installation
def deps do
[
{:apple_maps_server, "~> 0.1.0"}
]
endConfiguration
Get a Maps identifier and private key in your Apple Developer account (Apple's walkthrough), then:
config :apple_maps_server,
maps_id: System.get_env("APPLE_MAPS_ID"),
team_id: System.get_env("APPLE_TEAM_ID"),
key_id: System.get_env("APPLE_MAPS_KEY_ID"),
private_key: System.get_env("APPLE_MAPS_PRIVATE_KEY"),
# or: private_key_path: "/path/to/AuthKey_XXXX.p8"
base_url: "https://maps-api.apple.com",
token_ttl_seconds: 1800
Every public function also accepts per-call opts that override the
application config — useful in tests or for multi-tenant deployments.
Passing any config key in the opts bypasses the token cache for that call.
Usage
{:ok, res} = AppleMapsServer.search("coffee")
{:ok, res} = AppleMapsServer.search_autocomplete("cof")
{:ok, res} = AppleMapsServer.geocode("1 Infinite Loop, Cupertino, CA")
{:ok, res} = AppleMapsServer.reverse_geocode(%{latitude: 37.3318, longitude: -122.0312})
{:ok, res} = AppleMapsServer.directions("San Francisco, CA", "Cupertino, CA")
{:ok, res} = AppleMapsServer.etas("37.33,-122.03", ["37.77,-122.41", "37.80,-122.27"])
{:ok, tok} = AppleMapsServer.token()Typed responses
Opt in per call with decode: true:
{:ok, %{"results" => [%AppleMapsServer.Place{} = place | _]}} =
AppleMapsServer.geocode("1 Infinite Loop, Cupertino, CA", decode: true)
place.coordinate #=> %AppleMapsServer.Coordinate{latitude: 37.33, longitude: -122.03}Errors
| Shape | Meaning |
|---|---|
{:error, %AppleMapsServer.Error{status: 401}} |
Token rejected — check team/key IDs and .p8 |
{:error, %AppleMapsServer.Error{status: 403}} | Maps capability not enabled for the key |
{:error, %AppleMapsServer.Error{status: 422}} | Invalid request parameters |
{:error, %AppleMapsServer.Error{status: 429}} | Rate limited — back off |
{:error, {:missing_config, :team_id}} | Required config not set |
{:error, {:token_generation_failed, reason}} |
JOSE could not sign (usually malformed .p8) |
{:error, {:transport_error, reason}} | Network / connection failure |
Integration testing
Integration tests hit the real Apple API and are excluded by default:
APPLE_MAPS_ID=... \
APPLE_TEAM_ID=... \
APPLE_MAPS_KEY_ID=... \
APPLE_MAPS_PRIVATE_KEY_PATH=/path/to/AuthKey_XXXX.p8 \
mix test --only integrationTroubleshooting
- 401 unauthorized — JWT rejected. Verify
team_id,key_id, and thatprivate_keyis the.p8PEM (containsBEGIN PRIVATE KEY). Make sure thescope: "server_api"claim is present (this library sets it automatically). - 403 forbidden — Maps capability not enabled for the key, or the Maps ID doesn't match the key.
:token_generation_failed— Most often an invalid PEM..p8files already include PEM headers; do not re-wrap them.- 429 rate limited — Apple throttles aggressively; implement retry/backoff in your caller.
License
MIT — see LICENSE.