New: Photo Explorer · Find any mapped travel photo

Product update

Happy Fourth: Travel Achievements and Mapsake Friends

Happy Fourth of July to everyone celebrating in the United States. This release makes a private atlas more playful with explainable achievements and intentional friend-to-friend sharing.

By 10 minute read
Mapsake Achievements showing searchable travel milestones and medal progress

Happy Fourth of July

Happy Fourth of July to everyone celebrating in the United States!

This update is about two ways a private travel map can become more alive: Achievements give the owner new patterns to notice in their own history, while Mapsake Friends makes it possible to compare that history with people they actually know.

Neither feature changes the core rule. Mapsake is a personal atlas first. There is no public follower count, global leaderboard, or feed designed to keep strangers performing for one another.

Around 160 reasons to look at the map again

Mapsake now includes around 160 achievements. Some are familiar milestones: a first marked place, five countries, every continent, or a growing list of cities and airports. Others describe geographic groups, culture, nature, science, aviation, and the less obvious shapes hiding inside a travel history.

The catalog is intentionally broad. A person who takes road trips through nearby regions should still find meaningful progress; the system is not reserved for someone trying to collect every country.

Under the interface, achievement evaluation is a deterministic pass over the atlas. Each achievement defines the places or thresholds it cares about, Mapsake compares that definition with the current Visited and Lived record, and the result includes progress, unlock state, date, and medal tier. It does not depend on an analytics service or a remote rules engine.

That approach has a useful consequence: achievements work offline and update immediately after an import or manual change. It also makes them testable. The same fixture can always produce the same set of unlocks.

The catalog grew from 11 to around 160

The first achievement engine shipped with 11 straightforward milestones. It was enough to prove the loop: derive progress from the map, highlight what was unlocked, and show a progress bar for the rest. It was also too small to remain interesting for anyone with an established travel history.

The second pass introduced families and geographic groups. Country-count milestones became one stack instead of a wall of nearly identical cards. Continental completion and groups such as the European Union, Schengen Area, ASEAN, the G7, the Maghreb, and the Stans added goals that were about the shape of travel rather than only its volume.

The catalog then expanded into arts, music, science, technology, nature, geology, and economics. That is where it began to feel distinctly Mapsake. A jazz-history group and a space-program group can overlap geographically while telling completely different stories about why those places belong together.

The challenge was avoiding arbitrary trivia. Every group needs a defensible, fixed membership list and an explanation that makes sense after the novelty wears off. Membership lives in source rather than arriving from a changing web service, so a catalog update is reviewed and versioned like any other product behavior.

Around 160 is enough to make search and organization necessary. It is not a target that has to grow forever. A smaller coherent catalog is better than thousands of procedural badges no one can understand.

Unlock dates and families changed the interface

When the catalog reached roughly 80 items, a normal grid stopped working. Multiple thresholds from the same family crowded out everything else, and a long-time user could not tell which completion was new.

Mapsake now collapses earned achievements in a family into a stack, with the highest tier on top. Expanding it reveals the earlier milestones. Locked achievements remain visible as goals, but the default sort puts what is closest to completion first.

Unlock dates are recorded the first time an achievement becomes true. They survive later evaluations and travel inside the friend snapshot, which lets a friend's achievement page show when something happened without exposing a private event log. Existing users are baselined on first run so installing the update does not produce a confetti storm for every milestone their old map already satisfied.

That baseline behavior came from treating migration as part of the feature. A technically correct engine that announces 70 “new” achievements on launch would feel broken, even if every badge were mathematically deserved.

Progress should be understandable

A wall of locked icons is not very helpful, so the Achievements tab is organized around explanation:

  • Search by name and filter by category or continent.
  • Sort by what is closest, recently unlocked, or already complete.
  • Open an achievement to see its description, member places, map, and current progress.
  • Move through bronze, silver, gold, and platinum tiers where a family supports them.
  • Long-press an achievement to make a share card without publishing anything automatically.

Unlock celebrations are brief and tactile, and the full details remain available afterward. The tone is deliberately lighthearted and occassionally a little nerdy. It should feel like discovering something about the map, not receiving a performance review.

The detail screen also solved a trust problem. A title such as “Five Eyes” or “Ring of Fire” is not self-explanatory to everyone. Opening it shows the full description, the map region, the countries in the group, which ones count as reached, and the visit date range where available. For tiered families, bronze through platinum communicate progression without requiring four unrelated visual systems.

The map deliberately highlights missing members as well as reached ones. Progress is easier to understand when a person can see that a goal is one neighboring country away rather than merely reading “8 of 9.”

A celebration cannot own navigation

One implementation detail became visible during testing: achievement celebrations originally behaved like a modal summary. The cards looked tappable, but the celebration layer did not carry enough navigation state to open the underlying detail reliably.

The fix was to treat each award as the same achievement identity used by the main catalog and route it to the existing detail screen. Celebration remains a temporary presentation; the achievement model remains the durable source. It is a small example of a broader Mapsake rule: special effects should sit on top of the product, not fork it.

Friends, without building a social network

Mapsake Friends begins with an invite, not a searchable directory. A link carries a private share code rather than a name or account. Opening it creates a mutual connection: each person can see the other's shared map inside Mapsake.

Links work well across distance, while QR codes and nearby discovery cover the moment when two people are in the same room. The app publishes a compact travel snapshot for the connection instead of trying to synchronize two full local databases.

The first sharing model was not reliable enough

Friends originally used a separate CloudKit share for every relationship. That looked attractive because CloudKit handled access invitations, but it made mutual friendship awkward. One person could accept a share without the original sender receiving a dependable path back, and the system share URL was hard to turn into a predictable in-app flow.

The replacement model gives each profile a stable shared-map record addressed by an opaque code. An invite link opens the friend's record, caches the snapshot, publishes the acceptor's own map, and writes a small request back to the original code. Foreground and background refreshes complete the mutual connection.

The first version of that replacement used the stable code directly in the link. It worked, but a link could be reused forever if it escaped the intended conversation. One-time invite tokens now sit in front of the stable map identity. A consumed token cannot be used again, while the underlying friendship can continue to refresh.

This is an example of product and infrastructure changing together. “Share my map” is one button in the interface, but underneath it are universal-link routing, CloudKit record lookup, mutual request handling, local caching, deduplication, and a clear success or failure message. Hiding that machinery is the job; pretending it does not exist would make failures impossible to explain.

A friend's profile has real depth. It can include an interactive map, a Passport with lens, region, and year controls, achievements, and a comparison view showing overlap and the places unique to each person. A home pin is excluded unless its owner explicitly opts in to sharing it.

The friend screen initially reused a simpler globe and summary. That quickly felt like a preview rather than a relationship. The current surface uses the same map settings as the owner's Atlas, the same Passport calculations, and the same achievement detail model with a different data source.

Reusing those components exposed a useful abstraction: map and Passport screens should consume travel data, not assume that data belongs to the signed-in local profile. Once friend snapshots implemented that interface, lens filtering, year ranges, continent scopes, achievement progress, and comparisons could share behavior without granting a friend access to the owner's underlying database.

There was a less glamorous navigation bug along the way. A friend row was bound directly to a SwiftData model object, but CloudKit mirroring could refault that object and change its identity just as the navigation push began. Driving navigation by the friend's stable ID instead fixed the apparently dead tap. Distributed data bugs often surface as ordinary interface bugs.

Profile photos followed the same snapshot rule. A small avatar can be published with the shared profile and refreshed after an edit. If no photo is set, initials remain the fallback. Mapsake prompts for a recognizable name and photo before the first invite, but does not make either a gate.

A feed made from meaningful changes

The Friends tab is not a stream of uploaded posts. Mapsake periodically compares the latest friend snapshot with the cached one already on the device. If a friend has added places or unlocked achievements, those differences become a small local activity feed.

This snapshot-diff approach keeps the feature simple. Friends do not need to compose a post every time their atlas changes, and Mapsake does not need a content-ranking system. The feed records a useful event because the underlying travel history changed.

It also avoids adding another CloudKit record type for every feed event. The app already has the old and new snapshots. Comparing their place identifiers and achievement unlocks can derive the interesting changes locally, then keep a bounded feed in a small JSON store on the device.

Place additions and achievement unlocks are grouped into separate bursts even when they arrive in one refresh. That prevents a large import from producing dozens of nearly identical rows and makes the feed read like “Emma added Japan, Kyoto, and two more” rather than a database transaction log.

Notifications can be enabled per travel lens with a cooldown, so a burst of imported history does not become a burst of alerts. Pull to refresh remains available when someone wants to check deliberately.

Privacy is part of the feature, not a disclaimer

Travel histories can reveal homes, routines, and future plans. Friends therefore has to remain more explicit than a conventional social feature:

  • Adding someone requires an invite, QR scan, or nearby exchange.
  • The shared payload contains the map data needed for the friend experience, not a copy of the photo library.
  • Home sharing is separate and off by default.
  • Removing a friend removes the local relationship and its cached activity.
  • Core use of Mapsake still requires no Mapsake account.

The friend snapshot never includes original photo bytes. It contains the travel marks, profile fields, lens information, dates needed for scoped statistics, and achievement unlocks needed for the surfaces the friend can open. That boundary keeps a rich experience from becoming a photo-sharing service by accident.

Mapsake also distinguishes friend sharing from a public browser link. A friend connection is mutual and lives inside the app. A public map is a separate opt-in artifact for someone who wants a read-only link. Turning on one does not silently turn on the other.

Achievements and Friends make the atlas more playful, but they do not turn it into a public profile. That boundary is important enough to keep repeating as the app grows.

Try the update

Open Achievements to search the full catalog and inspect what is closest. Then open Friends, share an invite link or use the nearby and QR option, and compare maps with someone you know.

The best outcome is not a higher score. It is a conversation: the place both people loved, the region one person has never considered, or the strange little achievement neither knew they were close to unlocking.

Mapsake app icon

Independent developer of Mapsake, writing from the product, mapping, privacy, and Apple-platform work behind the app.

Build your own atlas

Start with the travel history you already have.

Mapsake is free, requires no Mapsake account for core use, and keeps supported photo matching on your device.

Get Mapsake free