iOS Example
A Swift/SwiftUI application that renders a MapLibre Native vector tile map and adds places search. Uses UIViewRepresentable to bridge MapLibre's UIKit view into SwiftUI.
Prerequisites
- Xcode 15+ (Swift 5.9+)
- iOS 16+ simulator or device
- A Geog API key (create one in the console)
Quick Start
git clone https://github.com/geogdev/examples.git
cd examples/mobile/ios-maplibre
open GeogExample.xcodeproj
Wait for Swift Package Manager to resolve dependencies, then add your API key to Config.xcconfig:
GEOG_API_KEY = gk_live_your-api-key-here
Press Cmd+R to build and run.
Project Structure
| File | Description |
|---|---|
Config.xcconfig | Build configuration — API key injected via build settings into Info.plist |
GeogExample/Services/GeogService.swift | API client — token exchange, caching at 80% TTL, and places search |
GeogExample/MapView.swift | UIViewRepresentable bridge — wraps MLNMapView with programmatic style |
GeogExample/PlacesSearchView.swift | SwiftUI search panel — search input, results list, and map marker management |
GeogExample/Models/Place.swift | Codable data model for search results |
GeogExample/ContentView.swift | Root SwiftUI view composing map and search |
How It Works
API Key Configuration
The API key is stored in Config.xcconfig and injected into the app through Xcode build settings → Info.plist. GeogService reads it from the bundle at launch:
let apiKey = Bundle.main.object(forInfoDictionaryKey: "GEOG_API_KEY") as! String
Token Exchange
GeogService exchanges the long-lived API key for a short-lived access token and caches it:
⚠️ Production note: This example embeds the API key in the app bundle for simplicity. In a production app distributed to end users, the API key can be extracted from the binary. Use a backend token-proxy instead — your server holds the long-lived key and returns short-lived tokens to the app. See Token Exchange API for details.
func getAccessToken() async throws -> String {
let now = Date()
if let cached = cachedToken, now < tokenExpiresAt {
return cached
}
var request = URLRequest(url: URL(string: "https://api.geog.dev/v1/auth/token")!)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: ["ttl": 3600, "scope": "tiles:read places:read"])
let (data, _) = try await URLSession.shared.data(for: request)
let response = try JSONDecoder().decode(TokenResponse.self, from: data)
cachedToken = response.accessToken
tokenExpiresAt = now.addingTimeInterval(Double(response.expiresIn) * 0.8)
return response.accessToken
}
Map Authentication
After obtaining an access token, the app configures MLNNetworkConfiguration so all tile requests include the Bearer header automatically:
let config = URLSessionConfiguration.default
config.httpAdditionalHeaders = [
"Authorization": "Bearer \(accessToken)"
]
MLNNetworkConfiguration.shared.sessionConfiguration = config
UIViewRepresentable Bridge
MapView wraps MLNMapView (UIKit) for use in SwiftUI using the UIViewRepresentable protocol with a Coordinator for delegate callbacks.
Full Source
github.com/geogdev/examples/tree/main/mobile/ios-maplibre
See Also
- Examples Overview — All examples and the integration pattern
- Token Exchange API — Endpoint specification
- Vector Tiles API — Tile endpoint reference
- Places API — Search endpoint reference
- Styles & Sprites — Hosted themes, sprites, and npm packages