[{"data":1,"prerenderedAt":242},["ShallowReactive",2],{"blog-\u002Fblog\u002Fnew-sdk-swift\u002F":3,"related-blog-\u002Fblog\u002Fnew-sdk-swift\u002F":204},{"id":4,"title":5,"abstract":6,"author":6,"body":7,"description":189,"excerpt":6,"extension":190,"head":6,"image":6,"keywords":191,"meta":196,"modified":6,"navigation":197,"path":198,"proficiencyLevel":6,"published":199,"rawbody":200,"schemaOrg":6,"schemaType":6,"seo":201,"stem":202,"__hash__":203},"blog\u002Fblog\u002Fnew-sdk-swift.md","New SDK for Swift is Now Available",null,{"type":8,"value":9,"toc":179},"minimark",[10,14,27,30,35,38,68,77,81,84,98,102,105,109,117,121],[11,12,5],"h1",{"id":13},"new-sdk-for-swift-is-now-available",[15,16,17,18,26],"p",{},"Today we are excited to announce the initial release of our ",[19,20,25],"a",{"href":21,"rel":22,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fswift\u002F",[23],"external","_blank","Swift"," SDK.",[15,28,29],{},"Xcode 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.",[31,32,34],"h2",{"id":33},"weve-listened-and-delivered","We've Listened and Delivered",[15,36,37],{},"With this latest addition, we now support multiple languages and platform types:",[39,40,41,49,56,63],"ul",{},[42,43,44],"li",{},[19,45,48],{"href":46,"rel":47,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fjavascript-typescript\u002F",[23],"JavaScript\u002FTypeScript",[42,50,51],{},[19,52,55],{"href":53,"rel":54,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fkotlin-jvm\u002F",[23],"Kotlin",[42,57,58],{},[19,59,62],{"href":60,"rel":61,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Fpython\u002F",[23],"Python",[42,64,65],{},[19,66,25],{"href":21,"rel":67,"target":24},[23],[15,69,70,71,76],{},"We also have a ",[19,72,75],{"href":73,"rel":74,"target":24},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@stadiamaps\u002Fmaplibre-search-box",[23],"MapLibre GL JS search box plugin",".",[31,78,80],{"id":79},"why-use-the-sdks","Why Use the SDKs?",[15,82,83],{},"Of course you could write your code to directly interface with our APIs, but there are several benefits to taking\nadvantage of our SDKs:",[39,85,86,89,92,95],{},[42,87,88],{},"Save time; no need to write your own API code since you have access to the full range of Stadia Maps geospatial APIs",[42,90,91],{},"Easily install via your favorite package manager",[42,93,94],{},"Faster access to documentation in your IDE with autocomplete",[42,96,97],{},"Always utilize the latest features because when we release a new API, we'll release an updated SDK",[31,99,101],{"id":100},"we-want-your-feedback","We Want Your Feedback",[15,103,104],{},"Since 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.",[31,106,108],{"id":107},"where-to-find","Where to Find",[15,110,111,112,76],{},"You can find the Swift SDK, and its source code, on\n",[19,113,116],{"href":114,"rel":115,"target":24},"https:\u002F\u002Fgithub.com\u002Fstadiamaps\u002Fstadiamaps-api-swift",[23],"GitHub",[31,118,120],{"id":119},"learn-more-next-steps","Learn More & Next Steps",[39,122,123,132,140],{},[42,124,125,126,131],{},"Read documentation about our ",[19,127,130],{"href":128,"rel":129,"target":24},"https:\u002F\u002Fdocs.stadiamaps.com\u002Fsdks\u002Foverview\u002F",[23],"SDKs"," and Plugins on our docs site.",[42,133,134,139],{},[19,135,138],{"href":136,"rel":137,"target":24},"https:\u002F\u002Fclient.stadiamaps.com\u002Fsignup\u002F?utm_source=marketing_site&utm_medium=blog&utm_campaign=sdk_launch&utm_content=swift_sdk_announcement",[23],"Create an account"," to start building today!",[42,141,142,143,148,149,154,155,160,161,166,167,172,173,178],{},"Join our community on ",[19,144,147],{"href":145,"rel":146,"target":24},"https:\u002F\u002Fslack.openstreetmap.us\u002F",[23],"Slack"," or ",[19,150,153],{"href":151,"rel":152,"target":24},"https:\u002F\u002Fdiscord.gg\u002FqRBy6qqtdT",[23],"Discord",", follow us\non ",[19,156,159],{"href":157,"rel":158,"target":24},"https:\u002F\u002Fen.osm.town\u002F@stadiamaps",[23],"Mastodon",", ",[19,162,165],{"href":163,"rel":164,"target":24},"https:\u002F\u002Ftwitter.com\u002F@stadiamaps",[23],"Twitter",", or\n",[19,168,171],{"href":169,"rel":170,"target":24},"https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fstadia-maps\u002F",[23],"LinkedIn",", or sign-up for our ",[19,174,177],{"href":175,"rel":176,"target":24},"https:\u002F\u002Feepurl.com\u002Fgs51fD",[23],"mailing list","!",{"title":180,"searchDepth":181,"depth":181,"links":182},"",4,[183,185,186,187,188],{"id":33,"depth":184,"text":34},2,{"id":79,"depth":184,"text":80},{"id":100,"depth":184,"text":101},{"id":107,"depth":184,"text":108},{"id":119,"depth":184,"text":120},"Helping iOS developers build modern mapping experiences even faster with Stadia Map SDKs.","md",[192,193,194,195],"Swift SDK","iOS Development","Xcode","Mobile SDK",{},true,"\u002Fblog\u002Fnew-sdk-swift","2023-07-20","---\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",{"title":5,"description":189},"blog\u002Fnew-sdk-swift","eitT75JQ5nnsdlKU2JyckTUl83DN1bNdZGp5ZyJMUx8",[205,217,228],{"title":206,"description":207,"path":208,"published":209,"keywords":210,"rawbody":216},"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",[211,193,212,213,214,215],"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":218,"description":219,"path":220,"published":221,"keywords":222,"rawbody":227},"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",[223,224,193,225,226],"SwiftUI","MapLibre","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":229,"description":230,"path":231,"published":232,"keywords":233,"rawbody":241},"Why Basic OpenStreetMap Routing Needs Real-Time Traffic","OpenStreetMap is a world-class road network, but without real-time traffic it's a static dataset. Here's why algorithmic ETAs fall apart in production logistics and how Stadia Maps closes the gap with TomTom-powered routing.","\u002Fblog\u002Fwhy-osm-routing-needs-real-time-traffic","2026-05-12",[234,235,236,237,238,239,240],"Routing","Navigation","OpenStreetMap","Traffic Data","Matrix Routing","Logistics","TomTom","---\ndescription: >-\n  OpenStreetMap is a world-class road network, but without real-time traffic\n  it's a static dataset. Here's why algorithmic ETAs fall apart in production\n  logistics and how Stadia Maps closes the gap with TomTom-powered routing.\nexcerpt: >-\n  OpenStreetMap is great geography, but without real-time traffic it falls\n  short on ETAs. Stadia Maps closes the gap with TomTom-powered routing.\npublished: \"2026-05-12\"\nkeywords:\n  - Routing\n  - Navigation\n  - OpenStreetMap\n  - Traffic Data\n  - Matrix Routing\n  - Logistics\n  - TomTom\nauthor:\n  name: \"Ian Wagner\"\n  jobTitle: \"Founder & President \u002F COO\"\n  sameAs:\n    - \"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fian-w-wagner\u002F\"\n---\n\n# Why Basic OpenStreetMap Routing Needs Real-Time Traffic\n\n> OpenStreetMap (OSM) provides a world-class geographic foundation, but it remains a static dataset. Without real-time traffic integration, routing engines must rely on algorithmic proxies—like road class and legal speed limits—which often lead to unreliable ETAs and logistics bottlenecks.\n\n## The Problem\n\n[OpenStreetMap (OSM)](https:\u002F\u002Fwww.openstreetmap.org\u002Fabout) is one of the world's leading road maps, but a persistent gap remains between fixed geographic data and a [live navigation experience](\u002Fproducts\u002Frouting-navigation\u002F). Without dedicated traffic data, Estimated Times of Arrival (ETAs) are essentially educated guesses. While OSM is excellent at mapping the world's road network, a static dataset cannot capture the actual driving conditions at this exact moment. In enterprise-grade logistics, the lack of live data is often the first significant technical hurdle.\n\n## The Limits of Algorithmic Guesswork\n\nIn the absence of real-time data, a routing engine must estimate travel speeds based on tags and a few common proxies:\n\n- **Road Class:** Assuming a motorway is always faster than a residential street.\n- **Tagged Speed Limits:** Using the legal maximum as the baseline (when the tag even exists).\n- **Network Density:** Adjusting for urban vs. rural environments.\n- **Time of Day:** Using low-granularity buckets like \"daytime\" and \"nighttime.\"\n\nReal-world data show wild variances compared to these static estimates. Road class is a blunt instrument for predicting speed. Missing speed limit tags in open datasets force routing engines to rely on broad averages, resulting in unreliable ETAs and logistics delays. Rule-based algorithms are also notoriously bad at predicting choke points because open datasets don't account for traffic light timings, congestion near specific exits, or the \"invisible\" friction of a busy intersection.\n\n## The Stadia Maps Difference\n\nTo move from guesswork to precision, we integrated [TomTom's global traffic data](https:\u002F\u002Fwww.tomtom.com\u002Fproducts\u002Ftraffic-apis\u002F) directly into the [Stadia Maps routing engine](https:\u002F\u002Fdocs.stadiamaps.com\u002Frouting\u002F). High-resolution historical profiles and live feeds allow for accurate, real-time routing. We provide this through three key technical pillars:\n\n1. **Global Coverage:** Access to consistent data across more countries than almost any other vendor.\n2. **Rapid Updates:** A traffic latency of approximately two minutes allows our API to suggest alternate routes almost as soon as a wreck occurs.\n3. **Historical Profiles:** Deep granularity forms the backbone of predictive routing. High-resolution historical data enables accurate, time-dependent routing in advance, allowing you to plan a route for Tuesday at 8:00 AM based on what might happen on Tuesdays at 8:00 AM.\n\n## Fleet Intelligence at Scale\n\nFor dispatch, optimization, and fleet operations, [matrix routing](https:\u002F\u002Fdocs.stadiamaps.com\u002Frouting\u002Ftime-distance-matrix\u002F) (calculating the time and distance between many origins and destinations) is the engine's most critical function.\n\nThe Stadia Maps infrastructure supports matrix requests that are significantly larger than most competitors allow on standard plans. By integrating traffic data directly into these large-scale requests, we eliminate the need for developers to split requests into smaller chunks, reducing unnecessary complexity and latency.\n\nDevelopers maintain full agency over their implementation. We provide the fastest route based on live conditions, but the frequency of re-routing remains entirely in your control. Choice of revalidation frequency puts you in charge of the trade-off between real-time accuracy and [scaling costs](\u002Fpricing\u002F), ensuring your bills remain as predictable as your ETAs.\n\n---\n\n[Create a free account](https:\u002F\u002Fclient.stadiamaps.com\u002Fsignup\u002F) to start building with real-time traffic and high-performance routing today. Our [documentation](https:\u002F\u002Fdocs.stadiamaps.com\u002Frouting\u002F) provides everything you need to integrate TomTom-powered precision into your existing OSM workflow.\n",1778676027031]