Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Build and test

on:
push:
branches: [ "main" ]
branches: ["main"]
pull_request:
branches: [ "main" ]
branches: ["main"]

permissions:
contents: write
Expand All @@ -20,8 +20,8 @@ jobs:
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
java-version: "21"
distribution: "temurin"

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v5
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/Release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ on:
permissions:
contents: read

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
release:
runs-on: ubuntu-latest
Expand All @@ -22,8 +26,8 @@ jobs:
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
java-version: "21"
distribution: "temurin"

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v5
Expand All @@ -35,7 +39,9 @@ jobs:

- name: Extract version from tag
id: version
shell: bash
run: |
set -euo pipefail
# Remove 'v' prefix if present (v0.2.3 -> 0.2.3)
VERSION="${GITHUB_REF_NAME#v}"
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
Expand Down
115 changes: 115 additions & 0 deletions .github/workflows/UpdateReadmeVersion.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
name: Update README version (Maven Central)

on:
# Keep README correct even if Central metadata lags a bit after release
schedule:
- cron: "23 6 * * *" # daily
workflow_dispatch: {}
# Best-effort: Central may lag behind a published GitHub release
release:
types: [published]

permissions:
contents: write

concurrency:
group: update-readme-version
cancel-in-progress: false

jobs:
update-readme:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Resolve latest release version from Maven Central metadata
id: resolve
shell: bash
run: |
set -euo pipefail

GROUP_ID="com.williamcallahan"
ARTIFACT_ID="apple-maps-java"

METADATA_URL="https://repo1.maven.org/maven2/${GROUP_ID//.//}/${ARTIFACT_ID}/maven-metadata.xml"

echo "Fetching: ${METADATA_URL}"
XML="$(curl -fsSL "$METADATA_URL")"

# Extract <release>...</release> (fallback to <latest> if needed)
RELEASE="$(printf '%s' "$XML" | sed -n 's:.*<release>\(.*\)</release>.*:\1:p' | head -n 1 || true)"
if [[ -z "${RELEASE}" ]]; then
RELEASE="$(printf '%s' "$XML" | sed -n 's:.*<latest>\(.*\)</latest>.*:\1:p' | head -n 1 || true)"
fi

if [[ -z "${RELEASE}" ]]; then
echo "Failed to resolve release version from maven-metadata.xml"
exit 1
fi

# Guardrail: don't ever write SNAPSHOT into README install snippets
if [[ "${RELEASE}" == *SNAPSHOT* ]]; then
echo "Resolved version is a SNAPSHOT (${RELEASE}); refusing to update README."
exit 1
fi

echo "version=${RELEASE}" >> "$GITHUB_OUTPUT"
echo "Resolved release version: ${RELEASE}"

- name: Update README.md snippets
shell: bash
run: |
set -euo pipefail

VERSION='${{ steps.resolve.outputs.version }}'

if [[ ! -f "README.md" ]]; then
echo "README.md not found at repo root"
exit 1
fi

# Replace only the dependency coordinates/version lines in the documented snippets.
# This avoids rewriting unrelated occurrences of old versions elsewhere.
perl -0777 -i -pe '
my $v = $ENV{VERSION};

# Gradle snippet: implementation("group:artifact:VERSION")
s/(implementation\(\"com\.williamcallahan:apple-maps-java:)[^\"\)]+(\"\))/$1$v$2/g;

# Maven snippet: <version>VERSION</version> within the apple-maps-java dependency block
s#(<dependency>\s*<groupId>com\.williamcallahan</groupId>\s*<artifactId>apple-maps-java</artifactId>\s*<version>)[^<]+(</version>\s*</dependency>)#$1$v$2#gms;

' README.md

echo "README.md updated to version ${VERSION}"

env:
VERSION: ${{ steps.resolve.outputs.version }}

- name: Detect changes
id: diff
shell: bash
run: |
set -euo pipefail
if git diff --quiet -- README.md; then
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "No changes to commit."
else
echo "changed=true" >> "$GITHUB_OUTPUT"
echo "README.md changed."
git --no-pager diff -- README.md
fi

- name: Commit and push
if: steps.diff.outputs.changed == 'true'
shell: bash
run: |
set -euo pipefail

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

git add README.md
git commit -m "docs(readme): update dependency version to ${{ steps.resolve.outputs.version }}"
git push
82 changes: 66 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
# Apple Maps SDK for Java (Server)
[![Maven Central](https://img.shields.io/maven-central/v/com.williamcallahan/apple-maps-java)](https://central.sonatype.com/artifact/com.williamcallahan/apple-maps-java)
[![CI](https://github.com/WilliamAGH/apple-maps-java/actions/workflows/CI.yaml/badge.svg)](https://github.com/WilliamAGH/apple-maps-java/actions/workflows/CI.yaml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md)
[![Context7](src/main/resources/static/img/context7-badge.svg)](https://context7.com/williamagh/apple-maps-java)
[![DeepWiki](src/main/resources/static/img/deepwiki-badge.svg)](https://deepwiki.com/WilliamAGH/apple-maps-java)

A lightweight Java SDK for the Apple Maps Server API, with automatic access-token exchange/refresh.
# Apple Maps Server SDK for Java

A lightweight, unofficial Java SDK for the [Apple Maps Server API](https://developer.apple.com/documentation/applemapsserverapi).
Designed for backend/JVM apps that need server-side geocoding, search, directions, or ETA via a REST API.

This SDK automatically exchanges your long-lived Apple-issued authorization token (JWT) for short-lived access tokens and refreshes as needed.

Note: this is **not** [MapKit](https://developer.apple.com/documentation/mapkit) (native UI SDK) or [MapKit JS](https://developer.apple.com/documentation/mapkitjs) (web UI SDK).
This project is not affiliated with Apple.

## Apple Maps ecosystem

Apple provides three primary ways to integrate Maps. This library supports #1 (Server API).

| Service | Purpose | Best for |
| :--- | :--- | :--- |
| [Apple Maps Server API](https://developer.apple.com/documentation/applemapsserverapi) | REST HTTP API | Backend services (Java/Kotlin/Python/etc.) that need geocoding, search, directions, or ETA data without a UI. |
| [MapKit](https://developer.apple.com/documentation/mapkit) | Native UI framework | iOS/macOS apps that need interactive maps. |
| [MapKit JS](https://developer.apple.com/documentation/mapkitjs) | JavaScript UI library | Web apps that need interactive maps in the browser. |

## Requirements

Expand All @@ -10,15 +32,13 @@ A lightweight Java SDK for the Apple Maps Server API, with automatic access-toke

## Installation

Replace `0.1.3` with the latest release.

Note: this repo’s build uses a Gradle Java toolchain (Java 17). If you don’t have JDK 17 installed locally, Gradle will download it automatically.
This repo’s build uses a Gradle Java toolchain (Java 17). If you don’t have JDK 17 installed locally, Gradle will download it automatically.

### Gradle

```groovy
dependencies {
implementation("com.williamcallahan:apple-maps-java:0.1.3")
implementation("com.williamcallahan:apple-maps-java:0.1.5")
}
```

Expand All @@ -28,11 +48,13 @@ dependencies {
<dependency>
<groupId>com.williamcallahan</groupId>
<artifactId>apple-maps-java</artifactId>
<version>0.1.3</version>
<version>0.1.5</version>
</dependency>
```

## Configure `APPLE_MAPS_TOKEN`
## Configuration

### `APPLE_MAPS_TOKEN` (required)

Option A (recommended): environment variable

Expand All @@ -54,6 +76,14 @@ cp .env-example .env

Then set `APPLE_MAPS_TOKEN=...` in `.env`. This repo’s Gradle build loads `.env` into system properties (useful for running integration tests locally). `.env` is ignored by git.

### `APPLE_MAPS_ORIGIN` (optional)

If your authorization token (JWT) was generated with a specific `origin` claim, set `APPLE_MAPS_ORIGIN` as well (this value is sent as the HTTP `Origin` header):

```bash
export APPLE_MAPS_ORIGIN="https://api.example.com"
```

## Verify your token (integration test)

Run the live integration test suite:
Expand All @@ -75,8 +105,10 @@ Or run a one-off CLI query:
```

More:
- Authorization/token details: `docs/authorization.md`
- Integration tests: `docs/tests.md`
- CLI usage: `docs/cli.md`
- More examples: `docs/usage.md`

## Example: geocode Startup HQ (San Francisco)

Expand All @@ -89,7 +121,14 @@ if (token == null || token.isBlank()) {
throw new IllegalStateException("Set APPLE_MAPS_TOKEN (env var) or APPLE_MAPS_TOKEN (system property).");
}

AppleMaps api = new AppleMaps(token);
String origin = System.getenv("APPLE_MAPS_ORIGIN");
if (origin == null || origin.isBlank()) {
origin = System.getProperty("APPLE_MAPS_ORIGIN");
}

AppleMaps api = (origin == null || origin.isBlank())
? new AppleMaps(token)
: new AppleMaps(token, origin);
PlaceResults results = api.geocode(GeocodeInput.builder("880 Harrison St, San Francisco, CA 94107").build());
System.out.println(results);
```
Expand Down Expand Up @@ -129,13 +168,15 @@ Example response (trimmed):
}
```

## What’s included
## Supported Server API features

- Geocode + reverse geocode
- Search + autocomplete (and `resolveCompletionUrls`)
- Directions + ETA
- Place lookup + alternate IDs
More examples: `docs/usage.md`
This SDK provides typed wrappers for common Apple Maps Server API operations:

- **Geocoding**: `geocode` and `reverseGeocode`
- **Search**: `search`
- **Autocomplete**: `autocomplete`, `resolveCompletionUrl`, and `resolveCompletionUrls`
- **Directions & ETA**: `directions` and `etas`
- **Places**: `lookupPlace`, `lookupPlaces`, and `lookupAlternateIds`

Common use case: business / startup search (name-only or name + address) via Search + Autocomplete.

Expand All @@ -149,11 +190,20 @@ Common use case: business / startup search (name-only or name + address) via Sea

## Quota (included limits)

Apple provides daily quotas per Apple Developer Program membership (for example, a service-call limit shared between MapKit JS service requests and Apple Maps Server API calls). When you exceed the daily service-call quota, Apple responds with HTTP `429 Too Many Requests`. Apple does not provide a self-serve way to purchase additional quota; for extra capacity, contact Apple via the Maps developer dashboard. https://maps.developer.apple.com/
Apple provides free daily quotas that are currently significantly more generous than Google Maps API. But you must have a paid Apple Developer Program membership, which costs $99/year for access to all its Apple ecosystem resources. This API is Apple hardware/software agnostic.

Apple applies a daily service-call limit per membership (for example, a quota shared between MapKit JS service requests and Apple Maps Server API calls). When you exceed the daily quota, Apple responds with HTTP `429 Too Many Requests`. Apple does not provide a self-serve way to purchase additional quota; for extra capacity, contact Apple via the [Maps developer dashboard](https://maps.developer.apple.com/).

## Built with Apple Maps Java

[![Brief with Apple Maps](src/main/resources/static/img/apple-maps-java-screenshot.png)](https://github.com/WilliamAGH/brief)

**[Brief](https://github.com/WilliamAGH/brief)** - Terminal AI chat client with `/locate` command powered by Apple Maps Java and rendered with [TUI4J](https://github.com/WilliamAGH/tui4j).

## More from the author

[William Callahan](https://williamcallahan.com)

- [TUI4J](https://github.com/WilliamAGH/tui4j) (a modern TUI library for Java)
- [Brief](https://github.com/WilliamAGH/brief) (an open-source terminal AI chat app in Java)
- [Other projects](https://williamcallahan.com/projects)
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ val javaTargetVersion = 17
group = providers.gradleProperty("GROUP").orNull ?: "com.williamcallahan"
version = providers.gradleProperty("version").orNull
?: providers.gradleProperty("VERSION_NAME").orNull
?: "0.1.4-SNAPSHOT"
?: "0.1.5"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
GROUP=com.williamcallahan
POM_ARTIFACT_ID=apple-maps-java
VERSION_NAME=0.1.4-SNAPSHOT
VERSION_NAME=0.1.5

POM_NAME=Apple Maps Java
POM_DESCRIPTION=Apple Maps Java implements the Apple Maps Server API for use in JVMs.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/main/resources/static/img/context7-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/main/resources/static/img/deepwiki-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading