[{"data":1,"prerenderedAt":210},["ShallowReactive",2],{"blog-\u002Fblog\u002Fmaplibre-swiftui-dsl\u002F":3,"related-blog-\u002Fblog\u002Fmaplibre-swiftui-dsl\u002F":176},{"id":4,"title":5,"abstract":6,"author":6,"body":7,"description":159,"excerpt":6,"extension":160,"head":6,"image":6,"keywords":161,"meta":166,"modified":6,"navigation":167,"path":168,"proficiencyLevel":169,"published":170,"rawbody":171,"schemaOrg":6,"schemaType":172,"seo":173,"stem":174,"__hash__":175},"blog\u002Fblog\u002Fmaplibre-swiftui-dsl.md","Our SwiftUI DSL Is Joining the MapLibre Family",null,{"type":8,"value":9,"toc":152},"minimark",[10,14,33,66,69,74,77,86,90],[11,12,5],"h1",{"id":13},"our-swiftui-dsl-is-joining-the-maplibre-family",[15,16,17,18,26,27,32],"p",{},"We're thrilled to announce ",[19,20,25],"a",{"href":21,"rel":22,"target":24},"https:\u002F\u002Fgithub.com\u002Fmaplibre\u002Fswiftui-dsl",[23],"external","_blank","MapLibre SwiftUI DSL","\nhas graduated from the Stadia Maps labs to become a ",[19,28,31],{"href":29,"rel":30,"target":24},"https:\u002F\u002Fmaplibre.org\u002F",[23],"MapLibre","-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.",[15,34,35,36,41,42,47,48,53,54,59,60,65],{},"The project began when our co-founder Ian Wagner wanted MapLibre to have a developer experience that rivaled MapKit for SwiftUI.\n",[19,37,40],{"href":38,"rel":39,"target":24},"https:\u002F\u002Fgithub.com\u002Farchdoog",[23],"Jacob Fielding"," (",[19,43,46],{"href":44,"rel":45,"target":24},"https:\u002F\u002Frallista.app\u002F",[23],"Rallista","),\n",[19,49,52],{"href":50,"rel":51,"target":24},"https:\u002F\u002Fsubzero.eu\u002F",[23],"Patrick Wolowicz"," and ",[19,55,58],{"href":56,"rel":57,"target":24},"https:\u002F\u002Fgithub.com\u002FPatrick-Kladek",[23],"Patrick Kladek","\n(",[19,61,64],{"href":62,"rel":63,"target":24},"https:\u002F\u002Fhudhud.sa\u002Fen",[23],"HudHud",") 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.",[15,67,68],{},"By 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.",[70,71,73],"h2",{"id":72},"modernizing-the-map-developer-experience","Modernizing the Map Developer Experience",[15,75,76],{},"Before 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.",[15,78,79,80,85],{},"For 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 ",[19,81,84],{"href":82,"rel":83,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fferrostar\u002F?utm_source=marketing_site&utm_campaign=swiftui_dsl_maplibre",[23],"Ferrostar navigation SDK","\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.",[70,87,89],{"id":88},"learn-more-next-steps","Learn More & Next Steps",[91,92,93,103,111,125],"ul",{},[94,95,96,97,102],"li",{},"Check out our ",[19,98,101],{"href":99,"rel":100,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fnative-multiplatform\u002Fmaplibre-gl-native\u002F?utm_source=marketing_site&utm_campaign=swiftui_dsl_maplibre#ios",[23],"quickstart","\nto build your first map with SwiftUI.",[94,104,105,106,110],{},"Find the source code on ",[19,107,109],{"href":21,"rel":108,"target":24},[23],"GitHub"," (and give it a star!).",[94,112,113,114,118,119,124],{},"Join the ",[115,116,117],"code",{},"#maplibre-swiftui-compose-playground"," channel in the ",[19,120,123],{"href":121,"rel":122,"target":24},"https:\u002F\u002Fslack.openstreetmap.us\u002F",[23],"OpenStreetMap US Slack"," to get help or join the development effort.",[94,126,127,128,133,134,139,140,145,146,151],{},"Follow Stadia Maps on ",[19,129,132],{"href":130,"rel":131,"target":24},"https:\u002F\u002Fen.osm.town\u002F@stadiamaps",[23],"Mastodon",", ",[19,135,138],{"href":136,"rel":137,"target":24},"https:\u002F\u002Ftwitter.com\u002F@stadiamaps",[23],"Twitter",",\nor ",[19,141,144],{"href":142,"rel":143,"target":24},"https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fstadia-maps\u002F",[23],"LinkedIn",", or sign-up for\nour ",[19,147,150],{"href":148,"rel":149,"target":24},"https:\u002F\u002Feepurl.com\u002Fgs51fD",[23],"newsletter"," to hear about exciting developments first.",{"title":153,"searchDepth":154,"depth":154,"links":155},"",4,[156,158],{"id":72,"depth":157,"text":73},2,{"id":88,"depth":157,"text":89},"Our SwiftUI DSL makes it easier than ever to build apps with maps. And now it's an official MapLibre project!","md",[162,31,163,164,165],"SwiftUI","iOS Development","Maps SDK","DSL",{},true,"\u002Fblog\u002Fmaplibre-swiftui-dsl","Beginner","2024-10-07","---\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","TechArticle",{"title":5,"description":159},"blog\u002Fmaplibre-swiftui-dsl","Ncwtkkjwjlne0gRj-6jbkPg78PNI_rB23R9yxDFd7N4",[177,189,200],{"title":178,"description":179,"path":180,"published":181,"keywords":182,"rawbody":188},"Ferrostar: Building a Cross-Platform Navigation SDK in Rust (Part 2 - iOS Packaging)","A deep dive into building and packaging a cross-platform navigation SDK for iOS.","\u002Fblog\u002Fferrostar-building-a-cross-platform-navigation-sdk-in-rust-part-2","2024-12-03",[183,163,184,185,186,187],"Rust","Swift Package","XCFramework","Cross-Platform Development","Ferrostar","---\ndescription: \"A deep dive into building and packaging a cross-platform navigation SDK for iOS.\"\npublished: \"2024-12-03\"\nkeywords:\n  - Rust\n  - iOS Development\n  - Swift Package\n  - XCFramework\n  - Cross-Platform Development\n  - Ferrostar\nschemaType: TechArticle\nproficiencyLevel: Expert\n---\n\n# Ferrostar: Building a Cross-Platform Navigation SDK in Rust (Part 2 - iOS Packaging)\n\nIt's been a while since our [first deep dive](\u002Fblog\u002Fferrostar-building-a-cross-platform-navigation-sdk-in-rust-part-1)\ninto the tech behind [Ferrostar](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fferrostar\u002F?utm_source=marketing_site&utm_campaign=ferrostar_tech_blog_2&utm_medium=blog), our new turn-by-turn navigation SDK.\nAs a recap, our [last post](\u002Fblog\u002Fferrostar-building-a-cross-platform-navigation-sdk-in-rust-part-1) covered why we're writing the core in Rust,\nhow we approached both code and data model sharing,\nand looked at the architecture from a high level.\n\nWriting some code for private use is one thing,\nbut publishing it for others to use can be maddeningly difficult.\nWhen the time came for us to publish Swift Packages,\nwe found something closer to the latter.\n\nIn this post, we'll cover the practical details of how we\ncross-compiled the Rust library for iOS,\npackaged it in an XCFramework,\nand published it as a Swift Package.\nThis is the missing manual we wish we had.\n\n![A screenshot of Ferrostar navigating on an iPhone](\u002Fimages\u002Fcontent\u002Fferrostar-landscape-screenshot.png)\n\n## Swift Packaging\n\nLet's start off by looking at the Swift Package structure.\nWe'll need to add _two_ targets to our `Package.swift`:\na binary target for the static library,\nand a Swift target for the generated bindings.\n\nBinary targets in Swift Package Manager can be tricky,\nand this complexity makes our `Package.swift` file a bit unwieldy from the start.\n\n```swift\nlet binaryTarget: Target\nlet useLocalFramework = false  \u002F\u002F NB: Set this to true when developing locally!\n\nif useLocalFramework {\n    binaryTarget = .binaryTarget(\n        name: \"FerrostarCoreRS\",\n        \u002F\u002F IMPORTANT: Swift packages importing this locally will not be able to\n        \u002F\u002F import Ferrostar core unless you specify this as a relative path!\n        path: \".\u002Fcommon\u002Ftarget\u002Fios\u002Flibferrostar-rs.xcframework\"\n    )\n} else {\n    \u002F\u002F Git stuff which we'll come back to at the end.\n    let releaseTag = \"0.23.0\"\n    let releaseChecksum = \"fa308b519db5424d73d00d60ca03fc18c1dcf2f88704aadce29259d12f2de2b2\"\n    binaryTarget = .binaryTarget(\n        name: \"FerrostarCoreRS\",\n        url:\n        \"https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fferrostar\u002Freleases\u002Fdownload\u002F\\(releaseTag)\u002Flibferrostar-rs.xcframework.zip\",\n        checksum: releaseChecksum\n    )\n}\n```\n\nUnfortunately, we haven't found a way to use the same `Package.swift`\nunmodified for both local development within the \"project\" and for publishing.\n(To be fair, this is an extremely rare feature,\nbut it's worth noting that Cargo workspaces let you do this seamlessly with Rust crates.)\nSo, we need to add some switching logic to our package definition.\n\nThe above is the solution we came up with.\nWe always ensure that `useLocalFramework = false` for the version checked in to git.\nPublished versions will need to point to the released artifact,\nor else we'd require every developer to check out and build the whole project locally.\n\nFor local development and CI builds,\nwhere you may need unreleased changes in the Rust core,\nyou have to override the value to `true`.\nIf anyone knows a cleaner solution to this, let us know!\n\nAnother complicating detail is that you _need_ a checksum for remote binary downloads.\nThis is a safety measure built into the Swift Package Manager\nto prevent supply chain attacks.\nSince the checksum can't be known until build time,\nwe have CI rewrite this line in `Package.swift` when publishing a release.\n(We'll cover this dance later.)\n\nOne target down... the other is fortunately a bit simpler.\nWe've called this `FerrostarCoreFFI` in our project,\nto make the distinction clear between the FFI bindings and the Rust binary.\n\n```swift\n.target(\n    name: \"FerrostarCoreFFI\",\n    dependencies: [.target(name: \"FerrostarCoreRS\")],\n    path: \"apple\u002FSources\u002FUniFFI\"\n)\n```\n\nThis is pure Swift and doesn't have any surprises.\nIt just depends on the binary target,\nand includes a source directory which will contain the generated bindings.\n\nYou'll need to add both the source and binary target to your list of `targets`.\nYou can now reference them by name from any of your other targets,\nand\u002For publish them via products.\n\n## Cross compilation\n\nNow that we've looked at the package structure,\nlet's see what actually goes into the two targets.\n\nWe'll tackle cross compilation first.\nWe're going to compile the Rust code for all the relevant iOS target architectures,\njust like Xcode does for our Swift projects.\n\nCross compilation is quite tricky (maybe even traumatic) in some languages,\nbut Cargo is full of pleasant surprises.\n\n```shell\ncargo build --lib --release --target some-target-triple\n```\n\nThat's it!\nJust specify the [target triple](https:\u002F\u002Fdoc.rust-lang.org\u002Fnightly\u002Frustc\u002Fplatform-support.html)!\nFor iOS, we actually need to compile for _three_ targets:\n\n| Target triple           | Description                    |\n|-------------------------|--------------------------------|\n| `x86_64-apple-ios`      | Simulator for Intel-based Macs |\n| `aarch64-apple-ios-sim` | Simulator for ARM-based Macs   |\n| `aarch64-apple-ios`     | iOS devices                    |\n\nIf you try running this on your machine right now,\nyou may get an error since you need to have the target toolchain installed.\nFortunately, this is easy.\nIf you're using `rustup`, you can add any target with the command\n`rustup target add your-target-triple`.\n\nFor iOS, there are just three targets,\nbut we'll have a LOT more by the time we're done with this series.\nRemembering all of these is a lot of work,\nbut there's a relatively under-appreciated file that can help us out:\n[`rust-toolchain.toml`](https:\u002F\u002Frust-lang.github.io\u002Frustup\u002Foverrides.html#the-toolchain-file).\n\nIf you're using `rustup` to install `cargo`,\nit will automatically install the relevant toolchains for you!\nWe can check this into git and call it a day.\nHere's [the full `rust-toolchain.toml` for Ferrostar](https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fferrostar\u002Fblob\u002Fmain\u002Fcommon\u002Frust-toolchain.toml).\n\nWhile this file is specifically designed for use with `rustup`,\nother build systems often have a way of consuming it too.\nFor example, Nix users could leverage [`fenix.fromToolchainFile`](https:\u002F\u002Fgithub.com\u002Fnix-community\u002Ffenix)\nto get the same effect.\n\n## Generating the FFI Bindings\n\nNow that we have the static library built in its first form,\nwe need an easy way for Swift to interface with it.\nIt's [UniFFI](https:\u002F\u002Fgithub.com\u002Fmozilla\u002Funiffi-rs)'s time to shine!\n\nSince our [first post](\u002Fblog\u002Fferrostar-building-a-cross-platform-navigation-sdk-in-rust-part-1), UniFFI has evolved a `uniffi-bindgen-swift` crate concept\nwith a slightly different CLI.\nNot a lot has changed, but it's worth a quick note in case you're on an older version.\nIf you're starting fresh, this [starter template](https:\u002F\u002Fgithub.com\u002Fianthetechie\u002Funiffi-starter)\nhas everything ready to go with the new setup.\n\nHere's how we generate the bindings:\n\n1. Run `uniffi-bindgen-swift` via `cargo run -p`.\n2. Copy the _generated Swift file_ to the appropriate source directory (the `FerrostarCoreFFI` target's source path in our example).\n3. Copy the _clang module map_ into a framework staging directory.\n\nHere's the relevant portion of our build script,\nwhere `$1` is the name of the library (`ferrostar` in our case).\n\n```zsh\ncargo run -p uniffi-bindgen-swift -- target\u002Faarch64-apple-ios\u002Frelease\u002Flib$1.a target\u002Funiffi-xcframework-staging --swift-sources --headers --modulemap --module-name $1FFI --modulemap-filename module.modulemap\nmv target\u002Funiffi-xcframework-staging\u002F*.swift ..\u002Fapple\u002FSources\u002FUniFFI\u002F\nmv target\u002Funiffi-xcframework-staging\u002Fmodule.modulemap target\u002Funiffi-xcframework-staging\u002Fmodule.modulemap\n```\n\n`uniffi-bindgen-swift` looks at the static library\nand generates everything we need to smoothly interact with it from Swift.\nThis includes enums, protocols, library loading code, etc.;\nthis is where it does the magic that makes the Rust library feel like a native Swift package.\nWe just need to copy everything into the location we said in our `Package.swift`.\n\nNOTE: It is possible to integrate these steps into Xcode.\nHowever, given the relative difficulty of doing this\nand the overall flakiness of the Xcode build process,\nwe opted for a simple, reliable shell script.\nThis does mean you need to manually rebuild when changing Rust files,\nbut that's a small step.\n\n## Building a \"fat\" library (universal binary)\n\nAs a last step before creating the XCFramework,\nwe need to do something a bit funny and fuse two of our three binary targets together.\n\nThe way that XCFramework is designed,\nit expects a single binary for each _platform_, not each _CPU architecture_.\nAnd we have two binaries for the iOS Simulator platform:\none for the newer Apple Silicon and one for Intel.\n\nTo get down to one binary per _platform_ (simulator and device),\nwe'll use an old tool, `lipo` to generate a \"fat\" binary with both architectures\nin a single file.\n\n```zsh\nlipo -create target\u002Fx86_64-apple-ios\u002Frelease\u002Flib$1.a target\u002Faarch64-apple-ios-sim\u002Frelease\u002Flib$1.a -output target\u002Fios-simulator-fat\u002Frelease\u002Flib$1.a\n```\n\n## Generating the XCFramework\n\nNow we finally put all of this together in an XCFramework.\nSome teams actually do this by hand,\nsince it's a relatively simple structure,\nbut we'll stick to Apple's official tooling.\n\n```zsh\nxcodebuild -create-xcframework \\\n    -library target\u002Faarch64-apple-ios\u002Frelease\u002Flib$1.a -headers target\u002Funiffi-xcframework-staging \\\n    -library target\u002Fios-simulator-fat\u002Frelease\u002Flib$1.a -headers target\u002Funiffi-xcframework-staging \\\n    -output target\u002Fios\u002Flib$1-rs.xcframework\n```\n\nThis command combines the two static libraries, header, and module map\ninto a single directory.\nAnd if all went well, we now have a working Swift package that you can test locally!\n\n## Distributing the XCFramework\n\nNow it's time to revisit the git and checksum dance that we glossed over at the start.\n\nHere's our distribution checklist:\n\n1. Zip up the folder.\n2. Compute a checksum for the archive.\n3. Update `Package.swift` with your release tag and checksum.\n\nWe script this in our CI actions like so:\n\n```zsh\nditto -c -k --sequesterRsrc --keepParent target\u002Fios\u002Flib$1-rs.xcframework target\u002Fios\u002Flib$1-rs.xcframework.zip\nchecksum=$(swift package compute-checksum target\u002Fios\u002Flib$1-rs.xcframework.zip)\nversion=$(cargo metadata --format-version 1 | jq -r --arg pkg_name \"$1\" '.packages[] | select(.name==$pkg_name) .version')\nsed -i \"\" -E \"s\u002F(let releaseTag = \\\")[^\\\"]+(\\\")\u002F\\1$version\\2\u002Fg\" ..\u002FPackage.swift\nsed -i \"\" -E \"s\u002F(let releaseChecksum = \\\")[^\\\"]+(\\\")\u002F\\1$checksum\\2\u002Fg\" ..\u002FPackage.swift\n```\n\n`ditto` is an archiving utility found on all macOS systems.\nWe use this to create the ZIP archive.\nThen, we compute the checksum using `swift package compute-checksum`.\nThis utility is included in the Xcode Command-line Tools.\nFor versioning, our repository prefers to keep all platforms in sync,\nand derives all versions from the Rust project using a bit of CLI magic.\nTo update `Package.swift`, we use trusty old `sed` to rewrite the relevant lines in-place.\n\nFor your package to be usable by others,\nyou'll need to host your XCFramework for download somewhere and update `Package.swift`\nwith an archive checksum and git tag.\nWe use GitHub's release artifact hosting since it's easy and free,\nbut you can also self-host the archive.\n\n## Wrap-up\n\nPackaging a binary framework for iOS isn't easy,\nand the best practices have evolved in the last few years.\nWhich is probably why no comprehensive (modern) guide exists for our use case!\nWe hope this helps anyone else shipping binary frameworks on iOS.\nWe've been using this process for about a year and a half for Ferrostar,\nand have automated all the steps with shell scripts and CI workflows.\n\nIf you're curious to try this in your own project,\ncheck out the [UniFFI starter](https:\u002F\u002Fgithub.com\u002Fianthetechie\u002Funiffi-starter),\nwhich includes ready-to-go build scripts and all the rest of the project boilerplate.\nAnd for a \"real-world\" CI pipeline on GitHub Actions,\ncheck out the [iOS Release action for Ferrostar](https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fferrostar\u002Fblob\u002Fmain\u002F.github\u002Fworkflows\u002Fios-release.yml).\n\nIn the next installment, we'll cover the build process and packaging for Android.\nGive us a follow on social media, join our Slack or Discord communities,\nor subscribe to our mailing list to get the news first!\n",{"title":190,"description":191,"path":192,"published":193,"keywords":194,"rawbody":199},"Autocomplete Search for SwiftUI and Jetpack Compose","We're making it easier than ever to add autocomplete search to your apps with customizable views for SwiftUI and Jetpack Compose.","\u002Fblog\u002Fautocomplete-search-for-swiftui-and-jetpack-compose","2024-10-14",[162,195,196,197,198],"Jetpack Compose","Autocomplete","Mobile Development","Search UI","---\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",{"title":201,"description":202,"path":203,"published":204,"keywords":205,"rawbody":209},"New SDK for Swift is Now Available","Helping iOS developers build modern mapping experiences even faster with Stadia Map SDKs.","\u002Fblog\u002Fnew-sdk-swift","2023-07-20",[206,163,207,208],"Swift SDK","Xcode","Mobile SDK","---\ndescription: \"Helping iOS developers build modern mapping experiences even faster with Stadia Map SDKs.\"\npublished: \"2023-07-20\"\nkeywords:\n  - Swift SDK\n  - iOS Development\n  - Xcode\n  - Mobile SDK\n---\n\n# New SDK for Swift is Now Available\n\nToday we are excited to announce the initial release of our [Swift](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fswift\u002F) SDK.\n\nXcode users will get all the goodies like autocomplete, type definitions, and documentation as you type. All API methods\nleverage the async features introduced in Swift 5.5, which make it easier than ever to write correct asynchronous code.\nAnd since it's generated from official OpenAPI spec, you'll be among the first to get access to our latest features.\n\n## We've Listened and Delivered\n\nWith this latest addition, we now support multiple languages and platform types:\n\n* [JavaScript\u002FTypeScript](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fjavascript-typescript\u002F)\n* [Kotlin](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fkotlin-jvm\u002F)\n* [Python](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fpython\u002F)\n* [Swift](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fswift\u002F)\n\nWe also have a [MapLibre GL JS search box plugin](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@stadiamaps\u002Fmaplibre-search-box).\n\n## Why Use the SDKs?\n\nOf course you could write your code to directly interface with our APIs, but there are several benefits to taking\nadvantage of our SDKs:\n\n* Save time; no need to write your own API code since you have access to the full range of Stadia Maps geospatial APIs\n* Easily install via your favorite package manager\n* Faster access to documentation in your IDE with autocomplete\n* Always utilize the latest features because when we release a new API, we'll release an updated SDK\n\n## We Want Your Feedback\n\nSince this is the initial version of the Swift SDK, we are looking for feedback from the community on updates, features,\nand improvements that we can incorporate into upcoming releases.\n\n## Where to Find\n\nYou can find the Swift SDK, and its source code, on\n[GitHub](https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fstadiamaps-api-swift).\n\n## Learn More & Next Steps\n\n- Read documentation about our [SDKs](https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Foverview\u002F) and Plugins on our docs site.\n- [Create an account](https:\u002F\u002Fclient.stadiamaps.com\u002Fsignup\u002F?utm_source=marketing_site&utm_medium=blog&utm_campaign=sdk_launch&utm_content=swift_sdk_announcement) 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)!\n\n",1778676026914]