The Pinpoint iOS SDK is a Swift package for FiRa compliant Ultra-Wideband (UWB) positioning with Pinpoint's technology.
The Pinpoint iOS can be used to integrate our indoor positioning system into your own solutions.
- Indoor Positioning for GNSS/GPS denied areas
- Accuracy of up to 30 cm
- Simple Integration
- UWB Background mode support
Before integrating the Pinpoint iOS SDK, please ensure you have access to the necessary Pinpoint hardware components.
The SDK requires compatible Pinpoint Hardware for accurate indoor positioning. Depending on your use case, you can use one of the following hardware options:
-
Prototyping Kit:
Ideal for developers and researchers who want to quickly evaluate and experiment with Pinpoint’s indoor positioning capabilities.
The kit includes all essential components required to set up a small-scale test environment. -
SATlets:
Compact satellite modules designed for scalable and permanent installations.
SATlets are suitable for production environments or larger deployments requiring reliable and precise indoor localization.
To ensure optimal performance, confirm that your hardware is correctly installed and configured before running the SDK.
Important The Pinpoint iOS SDK requires an iPhone with iOS 27beta as well as XCode27. Make sure your MacOS is at leat macOS 26.5 in order to run the latest XCode27 beta.
To integrate the Pinpoint iOS SDK add the repo as a swift package dependency to you project using the Swift Package Manager.
This requires a setup of a custom registry and your access credentials for the Pinpoint SDK repository.
- Add the registry:
swift package-registry set --global https://posie.pinpoint.de:8073/repository/ios_sdk_release/~/Library/org.swift.swiftpm/configuration/registries.json should look like this:
{
"registries" : {
"[default]" : {
"supportsAvailability" : false,
"url" : "https://posie.pinpoint.de:8073/repository/ios_sdk_release/"
}
},
"version" : 1
}- Add Credentials to Keychain
swift package-registry login https://posie.pinpoint.de:8073/repository/ios_sdk_release/login --username <username>Enter your Pinpoint repository password when prompted
- In Xcode -> Add Package Dependencies -> Enter
pinpoint.pinpointsdk-> Add to Project
Set the Dependency Rule to Exact Version.
To update the SDK, bump the version number to the latest, released Pinpoint SDK version.
To use the Pinpoint iOS SDK in your iOS project, follow the steps below.
We strongly recommend to use the included Sample App as implementation reference.
The usage examples below can be found in PositionProvider.swift inside the demo app.
First, import the module at the top of your Swift file:
import PinpointSDKThe PinpointApi class provides various functions to interact with nearby tracelets using Bluetooth.
Below are the main functions available for public use:
Initialize the API class with your API-Key.
The initialization requires to run ansynchronously.
It's recommended to initialize the API in a central place in your app and make it observable for other parts of your application.
The PinpointSDK offers two protocols to implement:
PinpointStateDelegate: Provides connection, BLE and license states updates.PinpointPositionDelegate: Provides position updates.
Make your class conform to PinpointStateDelegate, and PinpointPositionDelegate.
Hint
You can use different classes to implement the delegates. E.g. a
StateManagerand aPositionManager. In our example, we implement both delegates in one single class.
class PositionProvider: ObservableObject,PinpointStateDelegate, PinpointPositionDelegate {
private func initializeSDK() async {
do {
let sdk = try await PinpointAPI(apiKey: "YOUR-API-KEY")
self.api = sdk
self.api?.positionDelegate = self // assign self as delegate
self.api?.stateDelegate = self // assign self as delegate
print("SDK initialized successfully")
} catch {
self.api = nil
print("SDK initialization failed:", error)
}
}
The LocalPosition data model provides the following positioning values withing the UWB-network coverage:
var x:Double
var y:Double
var z:Double
var acc: Double
var lat:Double
var lon:Double
var alt:Double// Delegate method for PinpointPositionDelegate
func pinpointAPI(_ api: PinpointAPI, didUpdatePosition position: LocalPosition) {
self.handleNewPosition(position)
}
// Delegate methods for PinpointStateDelegate
func pinpointAPI(_ api: PinpointAPI, didChangeBLEState state: BLEState) {
print(state)
}
func pinpointAPI(_ api: PinpointAPI, didChangeConnectionState state: ConnectionState) {
DispatchQueue.main.async {
self.connectionState = state
}
}
// New: Returns the exiry date of the cached license.
// The cached licence is renewed automatically.
// In rare cases, when the phone has no internet connection for license renewal, this date should be
// observed and the user notified to connect to the internet.
func pinpointAPI(_ api: PinpointSDKFramework.PinpointAPI, didChangeLicenseState expiry: Date) {
print("License expires on: \(expiry.formatted())")
}
Altenatively, you can listen to changes within the published variable api.localPosition directly, when using SwiftUI
.onAppear {
// Your code here e.g.
// Set initial position
xPos = api.localPosition.x
yPos = api.localPosition.y
latPos = api.localPosition.lat
lonPos = api.localPosition.lon
}
.onChange(of: api.localPosition) { newPosition in
// Your code here e.g.
// Update position when localPosition changes
xPos = newPosition.x
yPos = newPosition.y
latPos = api.localPosition.lat
lonPos = api.localPosition.lon
}The connection, setup and position updates can be initiated with as single function call of startPositionStream(siteID:UInt32, blob:Data).
siteID: The Site ID from your site (shown in EasyPlan)blob: A.binfile that can be generated from EasyPlan
The blob needs to be converted to Data:
e.g.: blobData = try Data(contentsOf: pathToBinFile)
In our Sample App, the blob will be added via a simple file picker.
When the call was successful, you receveive continuous position updates via didUpdatePosition delegate method, you implemented before.
// Start the postion stream
func startPositionStream(siteID:UInt32, blob:Data) async {
guard let api = self.api else { return }
await api.startPositionStream(siteId: siteID, blob: blob)
}The UWB-session can be kept alive by using LiveActivites.
Set up a LiveActivity as described here:
https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities
You also need to add Background Modes capability to your target and check Uses Nearby Interaction in:
XCode -> Target -> Signing and Capabilities -> Add capability -> Background Modes.
A minimal set of ActivityAttributes for the LiveActivity can look like this:
import ActivityKit
import NearbyInteraction
struct WidgetAtributes: ActivityAttributes {
struct ContentState: Codable, Hashable {
var x: Double
var y: Double
}
}The included Sample App shows a minimal implementation of the LiveActivity with continuous background positioning.
This package is licensed under a proprietary license. Please refer to the LICENSE file for more details.



