[{"data":1,"prerenderedAt":195},["ShallowReactive",2],{"blog-\u002Fblog\u002Fautocomplete-search-for-swiftui-and-jetpack-compose\u002F":3,"related-blog-\u002Fblog\u002Fautocomplete-search-for-swiftui-and-jetpack-compose\u002F":161},{"id":4,"title":5,"abstract":6,"author":6,"body":7,"description":145,"excerpt":6,"extension":146,"head":6,"image":6,"keywords":147,"meta":151,"modified":6,"navigation":152,"path":153,"proficiencyLevel":154,"published":155,"rawbody":156,"schemaOrg":6,"schemaType":157,"seo":158,"stem":159,"__hash__":160},"blog\u002Fblog\u002Fautocomplete-search-for-swiftui-and-jetpack-compose.md","Autocomplete Search for SwiftUI and Jetpack Compose",null,{"type":8,"value":9,"toc":138},"minimark",[10,14,33,36,40,52,55,62,67,70,79,83],[11,12,5],"h1",{"id":13},"autocomplete-search-for-swiftui-and-jetpack-compose",[15,16,17,18,26,27,32],"p",{},"If you've ever tried adding location search to a mobile app,\nyou already know how challenging it can be to get the UI right,\njuggle network data sources, and other headaches.\nToday we're making that easier with composable UI elements\nfor ",[19,20,25],"a",{"href":21,"rel":22,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fswiftui-autocomplete-search\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch&content=intro",[23],"external","_blank","SwiftUI","\nand ",[19,28,31],{"href":29,"rel":30,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fjetpack-compose-autocomplete-search\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch&content=intro",[23],"Jetpack Compose",".",[15,34,35],{},"These packages build on top of our existing SDKs for Swift and Kotlin,\nand handle all the API communication for you.\nNo boilerplate.\nWith just a few lines of code, you'll have a UI that \"just works.\"\nAnd once the user selects a result from the list,\nyour code can decide what to do next.\nFor example, you can autofill an address form or start turn-by-turn navigation.",[11,37,39],{"id":38},"keeping-it-customizable","Keeping it customizable",[15,41,42,43,47,48,51],{},"Composable UI frameworks like SwiftUI and Jetpack Compose\nenable many customizations that are difficult or impossible with other approaches—\nall while keeping things easy to use.\nFor example, if you want to customize the look and feel of the list,\njust pass in your own ",[44,45,46],"code",{},"View"," or ",[44,49,50],{},"@Composable",", and we'll use that instead!\nAnd of course, you can use any SwiftUI or Jetpack Compose modifiers\nto change how the view is laid out in your app.",[15,53,54],{},"We also provide many customizations for you automatically.\nThe view appearance automatically syncs with your material theme or dark mode preferences,\nso it fits in with the rest of your app.\nAnd we even localize the UI based on device settings,\nso users will see results in their language wherever possible.",[15,56,57],{},[58,59],"img",{"alt":60,"src":61},"Autocomplete Search Example","\u002Fimages\u002Fcontent\u002Fautocomplete-search-localized.png",[63,64,66],"h2",{"id":65},"putting-results-in-contextprivately","Putting Results in Context—Privately",[15,68,69],{},"Location is critically important context if you want to provide relevant search results.\nFor example, \"London\" might conjure up images of Big Ben for some,\nbut maybe London, Ontario is a bit more relevant if you're in Toronto.\nThat's why our APIs can optionally focus results around a location.",[15,71,72,73,78],{},"Our new packages support this too and, just like our APIs,\nlocation information is optional.\nAnd if you're in the EU and concerned about GDPR compliance,\nthe new packages support our ",[19,74,77],{"href":75,"rel":76,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Feu-gdpr-endpoints\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch",[23],"EU endpoints","\nwith a simple flag.",[63,80,82],{"id":81},"learn-more-next-steps","Learn More & Next Steps",[84,85,86,98,111],"ul",{},[87,88,89,90,26,94,32],"li",{},"Check out our quickstart documentation for ",[19,91,25],{"href":92,"rel":93,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fswiftui-autocomplete-search\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch&content=next_steps",[23],[19,95,31],{"href":96,"rel":97,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fjetpack-compose-autocomplete-search\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch&content=next_steps",[23],[87,99,100,101,105,106,110],{},"Find the source code on GitHub (",[19,102,25],{"href":103,"rel":104,"target":24},"https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fswiftui-autocomplete-search",[23]," and ",[19,107,31],{"href":108,"rel":109,"target":24},"https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fjetpack-compose-autocomplete-search",[23],"; give it a star!).",[87,112,113,114,119,120,125,126,131,132,137],{},"Follow Stadia Maps on ",[19,115,118],{"href":116,"rel":117,"target":24},"https:\u002F\u002Fen.osm.town\u002F@stadiamaps",[23],"Mastodon",", ",[19,121,124],{"href":122,"rel":123,"target":24},"https:\u002F\u002Ftwitter.com\u002F@stadiamaps",[23],"Twitter",",\nor ",[19,127,130],{"href":128,"rel":129,"target":24},"https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fstadia-maps\u002F",[23],"LinkedIn",", or sign-up for\nour ",[19,133,136],{"href":134,"rel":135,"target":24},"https:\u002F\u002Feepurl.com\u002Fgs51fD",[23],"newsletter"," to hear about exciting developments first.",{"title":139,"searchDepth":140,"depth":140,"links":141},"",4,[142,144],{"id":65,"depth":143,"text":66},2,{"id":81,"depth":143,"text":82},"We're making it easier than ever to add autocomplete search to your apps with customizable views for SwiftUI and Jetpack Compose.","md",[25,31,148,149,150],"Autocomplete","Mobile Development","Search UI",{},true,"\u002Fblog\u002Fautocomplete-search-for-swiftui-and-jetpack-compose","Beginner","2024-10-14","---\ndescription: \"We're making it easier than ever to add autocomplete search to your apps with customizable views for SwiftUI and Jetpack Compose.\"\npublished: 2024-10-14\nkeywords:\n  - SwiftUI\n  - Jetpack Compose\n  - Autocomplete\n  - Mobile Development\n  - Search UI\nschemaType: TechArticle\nproficiencyLevel: Beginner\n---\n\n# Autocomplete Search for SwiftUI and Jetpack Compose\n\nIf you've ever tried adding location search to a mobile app,\nyou already know how challenging it can be to get the UI right,\njuggle network data sources, and other headaches.\nToday we're making that easier with composable UI elements\nfor [SwiftUI](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fswiftui-autocomplete-search\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch&content=intro)\nand [Jetpack Compose](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fjetpack-compose-autocomplete-search\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch&content=intro).\n\nThese packages build on top of our existing SDKs for Swift and Kotlin,\nand handle all the API communication for you.\nNo boilerplate.\nWith just a few lines of code, you'll have a UI that \"just works.\"\nAnd once the user selects a result from the list,\nyour code can decide what to do next.\nFor example, you can autofill an address form or start turn-by-turn navigation.\n\n# Keeping it customizable\n\nComposable UI frameworks like SwiftUI and Jetpack Compose\nenable many customizations that are difficult or impossible with other approaches—\nall while keeping things easy to use.\nFor example, if you want to customize the look and feel of the list,\njust pass in your own `View` or `@Composable`, and we'll use that instead!\nAnd of course, you can use any SwiftUI or Jetpack Compose modifiers\nto change how the view is laid out in your app.\n\nWe also provide many customizations for you automatically.\nThe view appearance automatically syncs with your material theme or dark mode preferences,\nso it fits in with the rest of your app.\nAnd we even localize the UI based on device settings,\nso users will see results in their language wherever possible.\n\n![Autocomplete Search Example](\u002Fimages\u002Fcontent\u002Fautocomplete-search-localized.png)\n\n## Putting Results in Context—Privately\n\nLocation is critically important context if you want to provide relevant search results.\nFor example, \"London\" might conjure up images of Big Ben for some,\nbut maybe London, Ontario is a bit more relevant if you're in Toronto.\nThat's why our APIs can optionally focus results around a location.\n\nOur new packages support this too and, just like our APIs,\nlocation information is optional.\nAnd if you're in the EU and concerned about GDPR compliance,\nthe new packages support our [EU endpoints](https:\u002F\u002Fdocs.stadiamaps.com\u002Feu-gdpr-endpoints\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch){ target=\"_blank\" }\nwith a simple flag.\n\n## Learn More & Next Steps\n\n- Check out our quickstart documentation for [SwiftUI](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fswiftui-autocomplete-search\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch&content=next_steps)\n  and [Jetpack Compose](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fjetpack-compose-autocomplete-search\u002F?utm_source=marketing_site&utm_campaign=composable_autocomplete_launch&content=next_steps).\n- Find the source code on GitHub ([SwiftUI](https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fswiftui-autocomplete-search){ target=\"_blank\" } and [Jetpack Compose](https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fjetpack-compose-autocomplete-search){ target=\"_blank\" }; give it a star!).\n- Follow Stadia Maps on [Mastodon](https:\u002F\u002Fen.osm.town\u002F@stadiamaps), [Twitter](https:\u002F\u002Ftwitter.com\u002F@stadiamaps),\n  or [LinkedIn](https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fstadia-maps\u002F), or sign-up for\n  our [newsletter](https:\u002F\u002Feepurl.com\u002Fgs51fD) to hear about exciting developments first.\n","TechArticle",{"title":5,"description":145},"blog\u002Fautocomplete-search-for-swiftui-and-jetpack-compose","2FSZAvysAK7rKMtVXFvFuA6a3J42dRXshuHYidWWaSk",[162,173,184],{"title":163,"description":164,"path":165,"published":166,"keywords":167,"rawbody":172},"Our SwiftUI DSL Is Joining the MapLibre Family","Our SwiftUI DSL makes it easier than ever to build apps with maps. And now it's an official MapLibre project!","\u002Fblog\u002Fmaplibre-swiftui-dsl","2024-10-07",[25,168,169,170,171],"MapLibre","iOS Development","Maps SDK","DSL","---\ndescription: \"Our SwiftUI DSL makes it easier than ever to build apps with maps. And now it's an official MapLibre project!\"\npublished: 2024-10-07\nkeywords:\n  - SwiftUI\n  - MapLibre\n  - iOS Development\n  - Maps SDK\n  - DSL\nschemaType: TechArticle\nproficiencyLevel: Beginner\n---\n\n# Our SwiftUI DSL Is Joining the MapLibre Family\n\nWe're thrilled to announce [MapLibre SwiftUI DSL](https:\u002F\u002Fgithub.com\u002Fmaplibre\u002Fswiftui-dsl)\nhas graduated from the Stadia Maps labs to become a [MapLibre](https:\u002F\u002Fmaplibre.org\u002F)-hosted project.\nThe project makes MapLibre a first-class citizen in SwiftUI apps,\nand fills an important gap in the iOS developer experience for MapLibre.\n\nThe project began when our co-founder Ian Wagner wanted MapLibre to have a developer experience that rivaled MapKit for SwiftUI.\n[Jacob Fielding](https:\u002F\u002Fgithub.com\u002Farchdoog){ target=\"_blank\" } ([Rallista](https:\u002F\u002Frallista.app\u002F){ target=\"_blank\" }),\n[Patrick Wolowicz](https:\u002F\u002Fsubzero.eu\u002F){ target=\"_blank\" } and [Patrick Kladek](https:\u002F\u002Fgithub.com\u002FPatrick-Kladek){ target=\"_blank\" }\n([HudHud](https:\u002F\u002Fhudhud.sa\u002Fen){ target=\"_blank\" }) shared the vision\nand joined the project, contributing significantly to the development and success of the project.\nWe're grateful for their ongoing contributions,\nand for the support from their respective companies.\n\nBy transitioning to a MapLibre hosted project,\nthe SwiftUI DSL will have more visibility,\nand will unify ongoing initiatives to make MapLibre work better with SwiftUI.\n\n## Modernizing the Map Developer Experience\n\nBefore the SwiftUI DSL, developers had to build their own wrapper views\nto use MapLibre in a SwiftUI app.\nThis was a lot of work—often duplicating what others had already done—and required a deep understanding of MapLibre Native's Objective-C API.\nThe SwiftUI DSL makes this largely a thing of the past.\nWith just a few lines of code, developers can overlay custom layers, bind the camera to reactive state, and set up custom gestures.\n\nFor library authors, the DSL unlocks a whole new level of composability.\nIt's now easy to offer a \"default\" experience that users can customize.\nOur own [Ferrostar navigation SDK](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fferrostar\u002F?utm_source=marketing_site&utm_campaign=swiftui_dsl_maplibre)\nleverages this pattern,\ngiving developers the power to add custom map layers, overlays, and controls\non top of a map during turn-by-turn navigation.\nThis sort of behavior is extremely difficult and error-prone in UIKit,\nbut is a breeze in SwiftUI.\nWe're excited to see more library authors adopting this approach too.\n\n## Learn More & Next Steps\n\n- Check out our [quickstart](https:\u002F\u002Fdocs.stadiamaps.com\u002Fnative-multiplatform\u002Fmaplibre-gl-native\u002F?utm_source=marketing_site&utm_campaign=swiftui_dsl_maplibre#ios){ target=\"_blank\" }\n  to build your first map with SwiftUI.\n- Find the source code on [GitHub](https:\u002F\u002Fgithub.com\u002Fmaplibre\u002Fswiftui-dsl){ target=\"_blank\" } (and give it a star!).\n- Join the `#maplibre-swiftui-compose-playground` channel in the [OpenStreetMap US Slack](https:\u002F\u002Fslack.openstreetmap.us\u002F) to get help or join the development effort.\n- Follow Stadia Maps on [Mastodon](https:\u002F\u002Fen.osm.town\u002F@stadiamaps), [Twitter](https:\u002F\u002Ftwitter.com\u002F@stadiamaps),\n  or [LinkedIn](https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fstadia-maps\u002F), or sign-up for\n  our [newsletter](https:\u002F\u002Feepurl.com\u002Fgs51fD) to hear about exciting developments first.\n",{"title":174,"description":175,"path":176,"published":177,"keywords":178,"rawbody":183},"Ferrostar: Building a Cross-Platform Navigation SDK in Rust (Part 1)","The first in a series of technical blog posts covering cross-platform mobile development in Rust.","\u002Fblog\u002Fferrostar-building-a-cross-platform-navigation-sdk-in-rust-part-1","2023-11-15",[179,149,180,181,182],"Rust","Navigation SDK","Cross-Platform Development","Ferrostar","---\ndescription: \"The first in a series of technical blog posts covering cross-platform mobile development in Rust.\"\npublished: \"2023-11-15\"\nupdated: \"2024-02-06\"\nkeywords:\n  - \"Rust\"\n  - \"Mobile Development\"\n  - \"Navigation SDK\"\n  - \"Cross-Platform Development\"\n  - \"Ferrostar\"\nschemaType: TechArticle\nproficiencyLevel: Expert\n---\n\n# Ferrostar: Building a Cross-Platform Navigation SDK in Rust (Part 1)\n\nThis is the first in a series of technical blog posts\ncovering the joys and challenges\nof building a cross-platform shared library,\nwith a focus on use in mobile applications.\n\nAs a motivating example to frame things,\nwe'll follow the journey of building Ferrostar,\na free and open-source navigation SDK.\nWe want to offer a modern SDK that is cross-platform,\nvendor-neutral, and easily extensible\nboth by direct contributors and developers using it in their apps.\n\n## Why?\n\nBefore we get into the technical weeds too much,\nit's worth taking a moment to ask _why_.\nAt Stadia Maps,\nwe have offered [highly customizable routing](\u002Fproducts\u002Frouting-navigation\u002F)\nsince 2017,\nand support use cases including safe pedestrian routing through lit areas,\ngolf cart routing that prefers multi-use paths,\nand more!\nBut developers are on their own\nwhen it comes to offering a turn-by-turn navigation UI.\nSeveral open-source apps already offer navigation,\nbut the solution is not generalized.\nAmong open-source navigation SDKs,\nMapLibre Navigation Android being the most mature at the time of this writing.\nBut as the name suggests, it is Android-specific.\nAnd since the internals originally came from old open-source Mapbox code,\nit takes considerable effort to use with other vendors,\nand extensibility was never a core design consideration.\n\nStarting a new navigation SDK is certainly an ambitious project,\nbut at the moment there is no navigation SDK that is\nhigh-quality, cross-platform, and open-source.\nWe're here to change that,\nand enable developers to bring these routing innovations to their users.\n\nThis post series is about the nuts and bolts of how we plan to pull it off.\nWe'll start with an overview of ways to share common logic across platforms,\nexplain the architecture we settled on,\nand finish with a deep dive into the first technical topic:\nbinding generation with [UniFFI](https:\u002F\u002Fmozilla.github.io\u002Funiffi-rs\u002F).\n\n## How to Share Common Logic?\n\nWe want to target multiple platforms: iOS and Android for starters.\nCertain code is the same regardless of where you're running it.\nAlgorithms like detecting if the user has strayed off the route\nand calculating the distance to the next turn\nshould be written once and shared across platforms.\nAnd at an even higher level,\nthe broad \"business logic\" of a turn-by-turn navigation experience\nshould be common shared code.\n\nBroadly speaking, there are two common approaches\nfor sharing code across platforms in mobile apps today.\nFirst, there are cross-platform app development frameworks like\nFlutter, React Native, or Kotlin Multiplatform.\nThey promise the ability to write your business logic and UI once\nand run it anywhere.\nThe second approach is to put your business logic in a shared library.\nYou write your code in a reasonably portable language (usually C++)\nand link this with your (platform-specific) applications.\n\nIn general,\nthe cross-platform frameworks are optimized for building *applications*,\nnot *libraries*.\nWe want to ensure that we can support additional platforms\nwith relative ease.\nYou can't just pick up React Native, for example,\nand run it on a bicycle computer.\nThis is situation is improving though.\nFor example, Flutter now runs on certain embedded devices,\nand Kotlin Multiplatform has a quickly improving support for library targets.\nHowever, these are still very early stage developments,\nand they involve a certain amount of ecosystem lock-in.\n\nGiven our requirements, we opted for the shared library approach.\nHistorically, C++ has been a popular choice for the task.\nIt is portable across a wide range of platforms,\ncan be made to interoperate with almost any other language,\nand usually produces small, well-optimized binaries.\nWe preferred to use something else for a few reasons,\nthe most important being\nthat it is difficult to write safe and correct C++ code,\nespecially as a new contributor.\n\nAt Stadia Maps, we have been shipping Rust in production since 2018.\nOur investment has rewarded us with\nconsistently good performance (Rust is keen on zero-cost abstractions),\nmemory safety,\nand excellent maintainability.\nAnd Rust code tends to have fewer bugs as well!\nRust also has excellent interoperability with other languages\nvia a straightforward FFI using the platform's C ABI,\nin stark contrast to C++ which can be rather complex to interface with.\nRust also has a large and growing list of supported platforms,\nincluding iOS and Android on several architectures.\n\nWe aren't alone in our selection of Rust either.\nWe've seen a lot of exciting development in Rust mobile frameworks\nlike [Crux](https:\u002F\u002Fredbadger.github.io\u002Fcrux\u002F),\n[Dioxus](https:\u002F\u002Fdioxuslabs.com\u002F),\nand [Rinf](https:\u002F\u002Fgithub.com\u002Fcunarist\u002Frinf) over the past year.\nBut it's not just new experimental projects that are adopting Rust.\nWe know that both [Mozilla](https:\u002F\u002Fwiki.mozilla.org\u002FOxidation)\nand [Lyft](https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=e2ID05dpJzo)\nare using Rust shared libraries in their mobile apps.\nRust is already being used in production mobile apps today.\n\n## Architecture Overview\n\nZooming out, let's look at the broader architecture we adopted.\nThe core is the place where we want all shared logic to live.\nIn the context of our navigation SDK,\nthis includes parsing API responses,\nfiguring out when to advance navigation to the next step,\ncalculating the distance to the next turn,\nand so on.\n\nShared logic in a \"functional core\" isn't new,\nbut we can go one step further and define our data models too!\nThis lets us keep the navigation logic vendor- and platform-agnostic.\nFor example,\nwe define a common location update type with fields like\ncoordinate, timestamp, and heading.\nThis way the core logic doesn't need to care about the differences between\n`CLLocation` and `android.location.Location`.\n\nWe're building a Rust library that's callable via the C ABI.\nBut if we want any mobile devs to even try our framework,\nwe'll need to expose a nicer Swift\u002FKotlin interface.\nThese are called bindings,\nand they provide a thin layer that hides the messy details\nof going back and forth between Rust and the \"native\" mobile code.\n\nFinally, we have the native mobile library.\nThis is the only thing that most app developers will see,\nand it handles communications with the \"outside world\" (ex: internet and GPS),\nand provides a higher level API that can be a bit more \"opinionated.\"\nWe won't say much more about this layer in the rest of our discussions,\nas many volumes have been written on good API and library design.\n\n**Spoiler to avert anxiety and frantic typing**: the rest of this blog goes deep into\n\"how the sausage is made.\"\nTo save you the tedious work of reproducing the structure for your next library,\nwe've put together a [public template repo](https:\u002F\u002Fgithub.com\u002Fianthetechie\u002Funiffi-starter)\non GitHub which is ready to go with no typing or other fiddling required.\nWith that, let's dive in!\n\n![Ferrostar Architecture Diagram](\u002Fimages\u002Fcontent\u002Fferrostar-arch.png)\n\n## Bindings: Bridging Rust and Platform Code\n\nIf you're familiar with the concept of foreign function interfaces (FFIs),\nyou may remember `extern` declarations,\ncalling convention specifications,\nand having to write a lot of boilerplate.\nBut you know what is really good at generating boilerplate?\nComputers!\nEnter: binding generators.\n\nMozilla has developed a fantastic tool for binding generation:\n[UniFFI](https:\u002F\u002Fmozilla.github.io\u002Funiffi-rs\u002F).\nIn their case, the motivation was to build pieces of Firefox\nwhich could be shared across platforms.\nUniFFI is designed to generate safe bindings that feel idiomatic\nto users of the target language.\nThe [design principles](https:\u002F\u002Fmozilla.github.io\u002Funiffi-rs\u002Finternals\u002Fdesign_principles.html)\nof UniFFI aligned well with our requirements for Ferrostar,\nand we think they tend to be a good fit for most similar mobile use cases.\n\n### Cargo workspace setup\n\nLet's dive in with discussing the cargo workspace setup for Ferrostar.\nThis should be a helpful guide for anyone wanting to create their own\ncross-platform library.\n\nWe chose to structure the Rust portion of our library as a workspace\nwith two member projects: `uniffi-bindgen` and `ferrostar`.\nAt the time of this writing,\nthe ability to run the binary from the `uniffi` crate\nis only available in nightly,\nso we opted for a second `uniffi-bindgen` crate in our workspace\nfor the CLI binary.\nHere's what it looks like on disk:\n\n```\n├── Cargo.lock\n├── Cargo.toml\n├── ferrostar\n│   ├── Cargo.toml\n│   └── src\n│       └── lib.rs\n└── uniffi-bindgen\n    ├── Cargo.toml\n    └── src\n        └── main.rs\n```\n\nScaffolding in place, we need to add UniFFI to our workspace's `Cargo.toml`.\nThis way we can keep the binding generator and library versions in sync.\nAt the time this article was originally published,\nproc macro features were still under active development.\nSince then, they have stabilized somewhat,\nand it might make sense to start using the crates.io releases.\nIf you want the newest features and fixes though,\nit's best to use git dependencies.\nTracking the latest commits can cause pain,\nbut it is pretty rewarding if you want access to the latest improvements,\nwhich do come quite fast.\n\n```toml\n[workspace]\n\nmembers = [\n    \"uniffi-bindgen\",\n    \"ferrostar\",\n]\nresolver = \"2\"\n\n[workspace.dependencies]\nuniffi = \"0.26.1\"\n```\n\nThen, we can add it to the `uniffi-bindgen` and `ferrostar` crates respectively.\nIn the `uniffi-bindgen` crate, we need the `cli` feature.\n\n```toml\n[dependencies]\nuniffi = { workspace = true, features = [\"cli\"] }\n```\n\nThe `main` function in `uniffi-bindgen\u002Fsrc\u002Fmain.rs` is a single line.\nHere's the entire file:\n\n```rust\nfn main() {\n    uniffi::uniffi_bindgen_main()\n}\n```\n\nIn the `ferrostar` crate, we also need `uniffi` as a normal dependency\n*and* build dependency.\nWe also need to configure the library target with a few different\n[`crate_type`s](https:\u002F\u002Fdoc.rust-lang.org\u002Freference\u002Flinkage.html).\n\n* `cdylib` - A dynamic system library. Used on most platforms.\n* `staticlib` - A system library with *all* upstream dependencies included.\n  Required to target iOS.\n* `lib` - A Rust library.\n  This isn't mentioned in the UniFFI documentation as of this writing,\n  but if you want to add integration tests (runnable via `cargo test`)\n  and you don't include a Rust library target,\n  you'll get strange errors.\n\n```toml\n[dependencies]\nuniffi.workspace = true\n\n[build-dependencies]\nuniffi = { workspace = true, features = [\"build\"] }\n\n[lib]\ncrate-type = [\"cdylib\", \"staticlib\", \"lib\"]\n```\n\n### Defining what to export\n\nInternally, UniFFI uses an IDL (Interface Definition Language) to describe\nthe public interface of your crate to foreign code (ex: Swift and Kotlin).\nThis is similar to the process of writing a header file in C\nin that it is a set of definitions,\nbut the definition file is used to generate the foreign language bindings.\n\nThis used to require writing a bunch of boilerplate by hand.\nFortunately, UniFFI (starting from v0.25.0) automates this using procedural macros.\nFor our use case,\nwe were able to replace *all* handwritten UDL with macros,\nand expect this will be the best path for most library authors going forward.\n\n#### Functions\n\nLet's get started by exporting a function from Rust\nwhich generates a parser for routing data coming from our APIs.\nA single macro, `uniffi:export` is all we need to export a top-level function.\n\n```rust\n#[uniffi::export]\nfn create_osrm_response_parser(polyline_precision: u32) -> Arc\u003Cdyn RouteResponseParser> {\n    Arc::new(OsrmResponseParser::new(polyline_precision))\n}\n```\n\nUniFFI macros generate the definitions for us automatically\nbased on the function's type signature.\nMany built-in types like numbers, `String`, and `bool`\n\"just work\" as you expect.\nCollections like `Vec\u003CT>`, `Option\u003CT>` and `HashMap\u003CK, V>` are also implemented\nfor all types that UniFFI knows how to represent over the FFI.\nThey even have special cases for types like `Vec\u003Cu8>`\nwhich idiomatically map to a language-specific byte sequence type\n(ex: `Data` in Swift)!\n\nIf anything you export references a type\nthat UniFFI doesn't know how to convert,\nyou will get a compilation error.\nSo, how do we tell UniFFI about new types?\n\n#### Data models\n\nLet's look at two data models: user locations and route requests.\nWe'll export these using derive macros.\nIf you've used the `serde` crate, this should feel familiar.\n\nUser location is just a struct with some properties like coordinates\nand course over ground.\nThe `uniffi::Record` macro exposes our Rust types idiomatically.\nThey will show up as `struct`s in Swift, and `data class`es in Kotlin.\n\n```rust\n#[derive(uniffi::Record)]\npub struct UserLocation {\n    pub coordinates: GeographicCoordinates,\n    pub horizontal_accuracy: f64,\n    pub course_over_ground: Option\u003CCourseOverGround>,\n    pub timestamp: SystemTime,\n}\n```\n\nRoute requests are a little different.\nThey don't really map to a record type.\nSince we want to build an extensible framework,\nwe need to account for multiple ways of getting a route.\nFor example, making an HTTP request, or computing a route on-device.\n\nThis maps well to an enumeration.\nThe `uniffi::Enum` derive macro exports these like you would expect.\n\n```rust\n#[derive(uniffi::Enum)]\npub enum RouteRequest {\n    HttpPost {\n        url: String,\n        headers: HashMap\u003CString, String>,\n        body: Vec\u003Cu8>,\n    },\n    \u002F\u002F ...\n}\n```\n\n#### Objects\n\nYou can also export more complex objects\nwith local state, methods, and constructors!\n\nOne example of this in Ferrostar is our route adapter.\nIt stores a request generator and response parser\nand exposes a generic interface\nso the caller doesn't need to know about the details.\n\n```rust\n#[derive(uniffi::Object)]\npub struct RouteAdapter {\n    request_generator: Arc\u003Cdyn RouteRequestGenerator>,\n    response_parser: Arc\u003Cdyn RouteResponseParser>,\n}\n\n#[uniffi::export]\nimpl RouteAdapter {\n    #[uniffi::constructor]\n    pub fn new(\n        request_generator: Arc\u003Cdyn RouteRequestGenerator>,\n        response_parser: Arc\u003Cdyn RouteResponseParser>,\n    ) -> Self {\n        Self {\n            request_generator,\n            response_parser,\n        }\n    }\n\n    #[uniffi::constructor]\n    pub fn new_valhalla_http(endpoint_url: String, profile: String) -> Arc\u003CSelf> {\n        let request_generator = create_valhalla_request_generator(endpoint_url, profile);\n        let response_parser = create_osrm_response_parser(6);\n        Self::new(request_generator, response_parser)\n    }\n\n    pub fn generate_request(\n        &self,\n        user_location: UserLocation,\n        waypoints: Vec\u003CGeographicCoordinates>,\n    ) -> Result\u003CRouteRequest, RoutingRequestGenerationError> {\n        self.request_generator\n            .generate_request(user_location, waypoints)\n    }\n\n    pub fn parse_response(\n        &self,\n        response: Vec\u003Cu8>,\n    ) -> Result\u003CVec\u003CRoute>, RoutingResponseParseError> {\n        self.response_parser.parse_response(response)\n    }\n}\n```\n\nThis example is a bit more complicated than the others we've looked at so far.\nFirst, we use the `uniffi::Object` derive macro on the struct.\nIn contrast to `uniffi::Record`,\nthis signals that the type will be passed by reference and may have methods.\nThis will be exposed in Swift or Kotlin as a class.\n\nYou must annotate your constructors with the `uniffi::constructor` macro.\nAt the time of this writing, constructors must return an `Arc\u003CSelf>`.\nThe `new` constructor is special, and is exposed most naturally,\nbut other named constructors are also supported if you mark them as such.\nThey are not currently as idiomatic as the default constructor,\nbut I expect things like exposing a `convenience init` to Swift\nwill be possible in the future.\n\nFinally, types exported with `uniffi::Object` tell the binding generator\nto emit a `protocol` (Swift) or `interface` (Kotlin) definition.\n**Pro tip:** This makes it easier for you to write mock implementations\nfor unit testing!\nOur rule of thumb is to _pass_ the protocol\u002Finterface to your native methods,\nand define your instance variables with protocol\u002Finterface types.\nWhen the time comes to construct one,\nyour business logic will dictate which _concrete_ implementation to construct.\nThis makes your code significantly more extensible,\nand allows you to either use implementations from the Rust core\nor define your own in Swift\u002FKotlin.\n\n#### Errors\n\nResult types in Rust signal errors,\nand UniFFI can expose these idiomatically in the generated bindings.\nWe use this pattern in the above example to signal response parsing errors.\n\n```rust\n#[derive(Debug, thiserror::Error, uniffi::Error)]\npub enum RoutingResponseParseError {\n    #[error(\"Failed to parse route response: {error}.\")]\n    ParseError { error: String },\n    \u002F\u002F ...\n    #[error(\"An unknown error parsing a response was raised in foreign code.\")]\n    UnknownError,\n}\n```\n\nThe `uniffi::Error` derive macro will work with any type\nconforming to `std::Error`.\nWe make extensive use of `thiserror` for simplicity.\nIn Swift and Kotlin code,\nthis definition will be bridged into an enum conforming to `Error`\nand a sealed subclass of `Exception` respectively.\n\nBesides exposing the error types themselves,\nUniFFI also makes the error handling idiomatic.\nLet's return to the response parsing function signature.\n\n```rust\npub fn parse_response(\n    &self,\n    response: Vec\u003Cu8>,\n) -> Result\u003CVec\u003CRoute>, RoutingResponseParseError>\n```\n\nSemantically speaking,\nthis function will either return an ordered list of routes,\nor it will generate an error of type `RoutingResponseParseError`.\nAs such, this is how the generated Swift bindings will look:\n\n```swift\npublic func parseResponse(response: Data) throws -> [Route]\n```\n\nSwift does not explicitly list error types, but uses the `throws` keyword.\nIf you inspect the generated code,\nyou can see that UniFFI does indeed handle error variants,\nmapping them to your error types.\n\n#### Traits\n\nCarrying on the motivating use case of vendor-agnostic routing API integration,\nwe define traits in our crate for response parsing\nand similar things that we want to ensure remain generic.\n\n```rust\n#[uniffi::export(with_foreign)]\npub trait RouteResponseParser: Send + Sync {\n    \u002F\u002F\u002F Parses a raw response from the routing backend into a route.\n    fn parse_response(&self, response: Vec\u003Cu8>) -> Result\u003CVec\u003CRoute>, RoutingResponseParseError>;\n}\n```\n\nYou can export traits with the `uniffi::export` macro,\nand will they show up as protocols\u002Finterfaces in your foreign code.\nEven better, if you use `with_foreign`, you can supply foreign code to Rust!\nIt requires a bit of a dance Rust-side\n(you must use types of the form `Arc\u003Cdyn Trait>` at the time of this writing),\nbut that's a small price to pay for such flexibility.\n\nHere's what it looks like in Swift.\nFirst, the Rust `trait` is translated into a Swift `protocol` automatically\nby the UniFFI binding generator.\nThe method even has `throws`, just like you'd write in a pure Swift project!\n\n```swift\npublic protocol RouteResponseParser: AnyObject {\n    \u002F**\n     * Parses a raw response from the routing backend into a route.\n     *\n     * We use a sequence of octets as a common interchange format.\n     * as this works for all currently conceivable formats (JSON, PBF, etc.).\n     *\u002F\n    func parseResponse(response: Data) throws -> [Route]\n}\n```\n\nNow we can implement it just like any other protocol.\nHere's an example of an implementation that we use in our testing.\nIt's a mock that is initialized with a fixed set of routes for\n\"end-to-end\" tests without hitting a network service.\nIn the real world, you might use `Codable`\nto convert the raw data into a Swift model\nand then map it to Ferrostar `Route`s.\n\n```swift\nclass MockRouteResponseParser: RouteResponseParser {\n    private let routes: [Route]\n\n    init(routes: [Route]) {\n        self.routes = routes\n    }\n\n    func parseResponse(response _: Data) throws -> [Route] {\n        routes\n    }\n}\n```\n\nBy making things like `RouteRequestGenerator` and `RouteResponseParser` traits,\nwe make it possible for anyone to do the following:\n\n1. Connect to a custom routing server (for example, one they are self-hosting).\n2. Parse routes in a different format.\n3. Generate routes on-device (in another library, Swift, Kotlin, etc.).\n\nWe're really excited about how this will enable more innovation by developers,\nsince Ferrostar will be easily adaptable to custom routing backends,\nboth for research and commercial use.\n\n## What works well?\n\nThe APIs UniFFI generates truly feel idiomatic!\nIt's hard to overstate how nice this is.\nIt also goes out of its way to generate protocols\u002Finterfaces,\nwhich make it easy to mock parts for unit testing in native code,\nand even has pleasant surprises\nlike `Vec\u003Cu8>` mapping to idiomatic byte sequence types.\n\nUniFFI encourages good hygiene all the way from Rust to your application code\nvia type safety.\nAny type that appears in your signature must be exported,\nso you get checks from end to end.\nAnd Rust functions that return a `Result` type\nare translated into idiomatic errors and exceptions.\n\nYou can even bridge custom types!\nWe don't have space to go deep into that here,\nbut here's some documentation on [how you can do it](https:\u002F\u002Fmozilla.github.io\u002Funiffi-rs\u002Flatest\u002Ftypes\u002Fcustom_types.html)\nwithout using primitives as a lowest common denominator.\n\nFinally, UniFFI makes it easy to use native code implementations\nof a protocol\u002Finterface with your Rust core!\nThis lets us do things like dependency injection in a type-safe manner\nacross an FFI boundary.\n\n## What are the rough edges?\n\nWhen I gave a talk about this three months ago at the Seoul Rust meetup,\nand I had an entire slide full of challenges.\nAlmost all of them were related to UDL,\nand almost are non-issues when you replace UDL generation with proc macros!\nA few remain though.\n\nFirst, **you'll need to change your habits around struct mutation**.\nSince we are introducing foreign code,\nUniFFI must assume that objects may be mutated from other threads.\nSo, you can't have mutable references to `self` in UniFFI interfaces\nand must rely on interior mutability patterns instead.\nImportantly, your storage needs to be both `Send` and `Sync`,\nwhich means you'll need to turn to things like atomics and mutexes.\n\nUniFFI is also evolving quite rapidly.\nWhile some high-profile crates are fairly stable pre-1.0,\nyou can definitely expect some turbulence with UniFFI.\nThe changelogs are normally quite good,\nbut if you're tracking `main`\nyou can expect breaking changes.\nFortunately, the team are _super_ responsive on GitHub.\nThey're working on it every day, and are extremely responsive as maintainers\n(we submitted a PR to improve some docs around proc macros,\nand it was merged after a round of discussions in less than 24 hours).\n\nThe process of generating bindings for Swift and Kotlin,\nintegrating this into your build tooling,\nand packaging everything into a usable SPM \u002F Maven package\nis also quite complex.\nIn fact, we would even suggest that the best practices around this\nare still being explored.\nThis is a complex topic, and this post is already quite long,\nso we'll save it for the next article.\n\n## Wrap-up\n\nIn summary, while it is still a developing field,\nwe couldn't be more excited about the future of Rust on mobile.\nAs we've shown in this post, a shared core is not just possible;\nit's actually pretty straightforward and the developer experience is great\nthanks to UniFFI.\nWe've put what we've learned into a\n[template repo](https:\u002F\u002Fgithub.com\u002Fianthetechie\u002Funiffi-starter)\nso you can try it too, without spending days figuring out boilerplate.\n\nStay tuned for the next post,\nwhere we'll dive deeper into build processes and packaging.\nYou can follow us on social media,\njoin our Slack or Discord communities,\nor subscribe to our mailing list (no spam)\nto get the news first.\n\nFinally,\nif you're excited to bring the latest innovations in routing to more users,\ncheck out Ferrostar [on GitHub](https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fferrostar).\nWe have an issue tracker and have even marked some good first issues.\n(We'd especially love to have someone who is excited about Jetpack Compose.)\nLet's build the future of mobile navigation together!\n",{"title":185,"description":186,"path":187,"published":188,"keywords":189,"rawbody":194},"Major Update to Our Geocoding and Autocomplete Search Data","We have improved over 50 million US addresses' completeness, accuracy, and searchability.","\u002Fblog\u002Fupdate-to-geocoding-autocomplete-search","2023-07-19",[190,148,191,192,193],"Geocoding","Address Data","US Addresses","Data Update","---\ndescription: \"We have improved over 50 million US addresses' completeness, accuracy, and searchability.\"\npublished: \"2023-07-19\"\nkeywords:\n  - Geocoding\n  - Autocomplete\n  - Address Data\n  - US Addresses\n  - Data Update\n---\n\n# Major Update to Our Geocoding and Autocomplete Search Data\n\nWe just updated the underlying data for our recent\n[Geocoding and Autocomplete Search APIs](https:\u002F\u002Fdocs.stadiamaps.com\u002Fgeocoding-search-autocomplete\u002Foverview\u002F).\n\nWith this release, our city and postal code data for over 50 million US addresses has significantly improved in terms of\ncompleteness, accuracy, and searchability.\n\n## Improved Quality and What This Means for Users\n\nThese improvements offer distinct advantages compared to our competitors' similar APIs in two key areas:\n\n### Enhanced Address Formatting\n\nThe results for US addresses will now more closely align with the preferred format recognized by the United States\nPostal Service, ensuring greater consistency and accuracy in address representation.\n\n### Improved Searchability\n\nWhen searching for addresses that have multiple colloquially used city names, users can effectively find the desired\naddresses by using these additional names, improving your search capabilities and overall user experience.\n\n## Learn More & Next Steps\n\n- Read documentation about our\n[Geocoding and Autocomplete Search APIs](https:\u002F\u002Fdocs.stadiamaps.com\u002Fgeocoding-search-autocomplete\u002Foverview\u002F) docs site.\n- [Create an account](https:\u002F\u002Fclient.stadiamaps.com\u002Fsignup\u002F?utm_source=marketing_site&utm_medium=blog&utm_campaign=geocoding_launch&utm_content=geocoding_updates_july) to start building today!\n- Join our community on [Slack](https:\u002F\u002Fslack.openstreetmap.us\u002F) or [Discord](https:\u002F\u002Fdiscord.gg\u002FqRBy6qqtdT), follow us\non [Mastodon](https:\u002F\u002Fen.osm.town\u002F@stadiamaps), [Twitter](https:\u002F\u002Ftwitter.com\u002F@stadiamaps), or\n[LinkedIn](https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fstadia-maps\u002F), or sign-up for our [mailing list](https:\u002F\u002Feepurl.com\u002Fgs51fD)!",1778676026881]