diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml
index 74d2b4e..0f91e86 100644
--- a/.github/workflows/CI.yaml
+++ b/.github/workflows/CI.yaml
@@ -2,9 +2,9 @@ name: Build and test
on:
push:
- branches: [ "main" ]
+ branches: ["main"]
pull_request:
- branches: [ "main" ]
+ branches: ["main"]
permissions:
contents: write
@@ -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
diff --git a/.github/workflows/Release.yaml b/.github/workflows/Release.yaml
index 64165ad..98ccc4b 100644
--- a/.github/workflows/Release.yaml
+++ b/.github/workflows/Release.yaml
@@ -7,6 +7,10 @@ on:
permissions:
contents: read
+concurrency:
+ group: release-${{ github.ref }}
+ cancel-in-progress: false
+
jobs:
release:
runs-on: ubuntu-latest
@@ -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
@@ -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
diff --git a/.github/workflows/UpdateReadmeVersion.yaml b/.github/workflows/UpdateReadmeVersion.yaml
new file mode 100644
index 0000000..041545b
--- /dev/null
+++ b/.github/workflows/UpdateReadmeVersion.yaml
@@ -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 ... (fallback to if needed)
+ RELEASE="$(printf '%s' "$XML" | sed -n 's:.*\(.*\).*:\1:p' | head -n 1 || true)"
+ if [[ -z "${RELEASE}" ]]; then
+ RELEASE="$(printf '%s' "$XML" | sed -n 's:.*\(.*\).*:\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 within the apple-maps-java dependency block
+ s#(\s*com\.williamcallahan\s*apple-maps-java\s*)[^<]+(\s*)#$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
diff --git a/README.md b/README.md
index a83554f..9f2e23d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,28 @@
-# Apple Maps SDK for Java (Server)
+[](https://central.sonatype.com/artifact/com.williamcallahan/apple-maps-java)
+[](https://github.com/WilliamAGH/apple-maps-java/actions/workflows/CI.yaml)
+[](LICENSE.md)
+[](https://context7.com/williamagh/apple-maps-java)
+[](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
@@ -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")
}
```
@@ -28,11 +48,13 @@ dependencies {
com.williamcallahanapple-maps-java
- 0.1.3
+ 0.1.5
```
-## Configure `APPLE_MAPS_TOKEN`
+## Configuration
+
+### `APPLE_MAPS_TOKEN` (required)
Option A (recommended): environment variable
@@ -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:
@@ -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)
@@ -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);
```
@@ -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.
@@ -149,7 +190,15 @@ 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
+
+[](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
@@ -157,3 +206,4 @@ Apple provides daily quotas per Apple Developer Program membership (for example,
- [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)
diff --git a/build.gradle.kts b/build.gradle.kts
index 79dd5e6..da64af4 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -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()
diff --git a/gradle.properties b/gradle.properties
index 5ffe9e4..22fea59 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -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.
diff --git a/src/main/resources/static/img/apple-maps-java-screenshot.png b/src/main/resources/static/img/apple-maps-java-screenshot.png
new file mode 100644
index 0000000..e960496
Binary files /dev/null and b/src/main/resources/static/img/apple-maps-java-screenshot.png differ
diff --git a/src/main/resources/static/img/context7-badge.svg b/src/main/resources/static/img/context7-badge.svg
new file mode 100644
index 0000000..28c8c49
--- /dev/null
+++ b/src/main/resources/static/img/context7-badge.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/main/resources/static/img/deepwiki-badge.svg b/src/main/resources/static/img/deepwiki-badge.svg
new file mode 100644
index 0000000..95548d0
--- /dev/null
+++ b/src/main/resources/static/img/deepwiki-badge.svg
@@ -0,0 +1 @@
+
\ No newline at end of file