Routing — USD 49, one-time
How far, and how long, to each of these.
One origin, many destinations, one call. Real road distance and travel time — the numbers you need to order a list of stores, pickup points or drivers by how long it actually takes to get there, not by a straight line on a map.
iOS routes on the device through MapKit, free and with no key. Android has no on-device routing engine at all — Play Services Location only positions, the Maps SDK only draws, and the google.navigation: intent hands the user to the Maps app without returning anything.
So on Android the plugin talks to a routing service, and the plugin ships the adapter for five of them. The account is yours: billed to you, under that provider's terms, and any attribution they require is yours to display. We carry no quota and no key. Without a provider configured, Android falls back cleanly and tells your app so.
Providers on Android
| Mapbox | Matrix API. The free tier is metered in elements — origins × destinations. |
|---|---|
| openrouteservice | Results are licensed CC-BY-SA and attribution is required. Check both against your use before choosing it. |
| Geoapify | Commercial use allowed on the free tier, with visible attribution. |
Routes computeRouteMatrix. Billed per element. | |
| OSRM | Your own osrm-backend, or any endpoint speaking its table/route shape — including your own API proxying one of the above. |
Switching provider is one line of config. Each one speaks a different dialect — Geoapify indexes by target_index, Google returns an array with the duration as a string — and the adapters translate, so your app sees one shape.
A key in the app config ships inside the build, where anyone holding the APK can read it. For production, point the OSRM adapter at your own backend instead: the key stays on your server, results can be cached per area, and you can swap provider without shipping a new version.
What it does
- Batch, not one-by-one. Up to 25 destinations resolved in a single request, with a per-destination fallback when the server refuses a batch.
- The event always fires. On both platforms, once per call, one row per destination. A destination that could not be routed comes back with
ok: falseand a reason —unsupported,timeout,no_route,over_limit,failed. Your app falls back to a straight-line estimate on a signal, never on a timeout that never arrives. - Every request is bounded. Each destination has its own time budget, so one stalled route cannot hold the batch.
- Ask before you render.
isSupported()tells you up front whether this device can route, so you can hide an ETA column instead of filling it with blanks. - Authenticated endpoints. Send headers with every routing request, so the endpoint you point at can be your own API behind a token instead of an open server.
- Driving and walking.
How it looks
use Guppylab\Directions\Facades\Directions;
use Guppylab\Directions\Events\DistancesReceived;
Directions::distances($lat, $lng, [
['id' => 1, 'lat' => -23.5614, 'lng' => -46.6559],
['id' => 2, 'lat' => -23.5880, 'lng' => -46.6820],
]);
#[OnNative(DistancesReceived::class)]
public function onDistances(DistancesReceived $event): void
{
if ($event->failed()) {
$this->useStraightLineEstimate();
return;
}
$this->etas = $event->routed(); // ['1' => ['meters' => 4312, 'seconds' => 780, …]]
}
Platforms
| iOS | MapKit MKDirections, iOS 15+. On-device, no key, no quota, nothing to configure. |
|---|---|
| Android | API 21+, plus an account with one of the providers above. Requires INTERNET, which the plugin declares. |
| NativePHP Mobile | 3.x |
Licence and delivery
- One-time payment of USD 49. No subscription.
- One legal entity, unlimited applications, distributed in compiled form.
- Installed with Composer from a private repository; credentials arrive with your purchase.
- Full source — PHP, Swift, Kotlin.
- Routing providers are billed by them and governed by their terms. Guppylab carries no quota and no credential on your behalf.
Not sure it fits? Ask first: suporte@guppylab.com.br