# Release Notes Source: https://www.nianticspatial.com/docs/nsdk/release_notes/?platform=kotlin # Release Notes 4.5.0 ## Highlights - [Play back Scaniverse playback sessions](#play-back-scaniverse-playback-sessions) - [Native playback on every platform](#native-playback-on-every-platform) - [Localize to a specific VPS map asset](#localize-to-a-specific-vps-map-asset) - [List site scans and non-production assets](#list-site-scans-and-non-production-assets) - [Mesh download caching and memory budget](#mesh-download-caching-and-memory-budget) - [Kotlin and Swift SDK hardening](#kotlin-and-swift-sdk-hardening) ## Features/Fixes ### Play back Scaniverse playback sessions Developers can now download a playback session recorded in Scaniverse and play it back. The recording is cached on the device, and the download reports its progress. In Swift and Kotlin, open it with `PlaybackDataset.open(scanId:onProgress:)` ([Swift](https://www.nianticspatial.com/docs/api/swift/NSDK.class-PlaybackDataset)) or `PlaybackDataset.open(scanId, onProgress)` ([Kotlin](https://www.nianticspatial.com/docs/api/kotlin/com.nianticspatial.nsdk.playback.PlaybackDataset)). In Unity, open it with `PlaybackDataset.OpenAsync(...)`, then assign the dataset to `RuntimeNsdkSettings.PlaybackDataset`. A cache session lets you check whether a recording is cached, list and delete cached recordings, and read a recording's frame count, size, and duration before downloading. It is `NSDKPlaybackCacheSession` in Swift, `NSDKSession.playbackCache` in Kotlin, and `PlaybackCacheClient` in Unity. ### Native playback on every platform Swift, Kotlin, and Unity now read playback datasets through the native playback reader, so all platforms parse a recording the same way. In Swift and Kotlin, `PlaybackDataset.open(datasetPath)` replaces the capture.json loaders, which are now deprecated. It exposes dataset `info` and per-frame `PlaybackFrameMetadata`, and Kotlin also opens `asset://` paths from the APK. Unity's `PlaybackDataset` is now public, and Unity playback no longer accepts `.zip` dataset paths; see [Breaking Changes](#breaking-changes) and [Setting up playback](https://www.nianticspatial.com/docs/nsdk/how-to/playback/setting_up_playback/). ### Localize to a specific VPS map asset VPS2 can now localize against one VPS map asset of a site by its UUID, including assets that are not in production. Use `localize(siteId:assetId:)` in [Swift](https://www.nianticspatial.com/docs/api/swift/NSDK.class-NSDKVps2Session), `localize(siteId, assetId)` in [Kotlin](https://www.nianticspatial.com/docs/api/kotlin/com.nianticspatial.nsdk.vps2.Vps2Session), or `TryLocalize(siteId, assetId)` in [Unity](https://www.nianticspatial.com/docs/api/unity/NianticSpatial.NSDK.AR.VPS2.ARVps2Manager). Stopping localization now cancels the site's requests that are still in flight. Universal localization can also use areas mapped by aerial capture when `aerialLocalizationEnabled` is set in Swift, `enableAerialLocalization` in [Kotlin](https://www.nianticspatial.com/docs/api/kotlin/com.nianticspatial.nsdk.Vps2Config), or `AerialLocalizationEnabled` in Unity. ### List site scans and non-production assets Sites can now list a site's scans and fetch one scan's details as `ScanInfo`. Use `requestScansForSite` and `requestScanInfo` in [Swift](https://www.nianticspatial.com/docs/api/swift/NSDK.class-NSDKSitesSession) and [Kotlin](https://www.nianticspatial.com/docs/api/kotlin/com.nianticspatial.nsdk.sites.SitesSession), or `RequestScansForSiteAsync` and `RequestScanInfoAsync` on [`SitesClient`](https://www.nianticspatial.com/docs/api/unity/NianticSpatial.NSDK.AR.Sites.SitesClient) in Unity. Location queries take a new `AssetScope` to include non-production assets; production-only remains the default. See [Getting started with Sites](https://www.nianticspatial.com/docs/nsdk/how-to/sites/getting_started/). ### Mesh download caching and memory budget Mesh downloads can now be cached on disk, which avoids downloading the same geometry and textures again. Enable the cache with `configure(with:)` on [`NSDKMeshDownloader`](https://www.nianticspatial.com/docs/api/swift/NSDK.class-NSDKMeshDownloader), `configure(MeshDownloaderConfig)` on [`MeshDownloaderSession`](https://www.nianticspatial.com/docs/api/kotlin/com.nianticspatial.nsdk.mesh.MeshDownloaderSession), or `ConfigureCache` on [`LocationMeshManager`](https://www.nianticspatial.com/docs/api/unity/NianticSpatial.NSDK.AR.Subsystems.LocationMeshManager). Asset mesh downloads now stop at a decoded-size budget, 256 MB by default and adjustable through `maxDecodedSizeKb`, and meshes decode on a background thread to lower peak memory. See [Mesh download](https://www.nianticspatial.com/docs/nsdk/how-to/vps/mesh_download/). ### Kotlin and Swift SDK hardening On Kotlin, [`NSDKSession`](https://www.nianticspatial.com/docs/api/kotlin/com.nianticspatial.nsdk.NSDKSession) can now be closed safely more than once and from any thread, and feature Flows such as `Vps2Session.localizationUpdates` survive `close()` and `acquire()`. The published Maven POM now declares its dependencies, and the AAR no longer adds storage permissions to apps. On Swift, the SDK is ready for Swift 6 strict concurrency, with `Sendable` value types and sessions and `@Sendable` progress callbacks. ## Changelog ### Added - Added `localize(siteId, assetId)` to localize against one VPS map asset by UUID, including non-production assets (Unity `TryLocalize`). - Added an `AssetScope` parameter to `requestSiteAssetsByLocation` to list non-production assets nearby; it defaults to `Production`. - Added `requestScansForSite` and `requestScanInfo` to list a site's scans and fetch scan details as `ScanInfo` (Unity `RequestScansForSiteAsync`, `RequestScanInfoAsync`). - Unity: Added the `HttpUnauthorized` Sites error for a missing, expired, or invalid access token. - Added an opt-in on-disk mesh download cache, configured with `MeshDownloaderSession.configure` (Swift `configure(with:)`, Unity `LocationMeshManager.ConfigureCache`). - Added a `maxDecodedSizeKb` budget to asset mesh downloads, capping the decoded memory of delivered chunks. - Swift, Kotlin, Unity: Added `aerialLocalizationEnabled` (Kotlin `enableAerialLocalization`, Unity `AerialLocalizationEnabled`) to localize against areas mapped by aerial capture. - Swift, Kotlin: Added `PlaybackDataset.open(scanId, onProgress)` to download, cache, and open a Scaniverse playback session. - Swift, Kotlin: Added a playback cache session (`NSDKPlaybackCacheSession`, Kotlin `NSDKSession.playbackCache`) with `isCached`, `cachedPlaybacks`, `playbackFileInfo`, `deleteCached`, and `clearCache`. - Swift, Kotlin: Added `PlaybackDataset.open(datasetPath)`, which reads datasets through native playback and exposes `info` and per-frame `PlaybackFrameMetadata`. - Kotlin: Added `asset://` APK asset paths to `PlaybackDataset.open` and made `PlaybackDataset` `AutoCloseable`. - Unity: Added `PlaybackCacheClient` and `PlaybackDataset.OpenAsync` to open Scaniverse playback sessions, with `IsCached`, `GetCachedPlaybacks`, `DeleteCached`, and `ClearCache`. - Unity: Made `PlaybackDataset` public and `IDisposable`, with `FrameCount`, `FrameRate`, and `Close()`. - Unity: Added `RuntimeNsdkSettings.PlaybackDataset` to play an app-opened `PlaybackDataset` from the next XR initialization. - Unity: Added `AreaId` to `Vps2LocalizedAsset` and `XRVps2AssetTrackingData`. - Unity: Added `MeshChunkData`, `GetLocationMeshChunkDataByVpsMapAssetIdAsync`, and `CreateMeshChunk` on `LocationMeshManager` to stream mesh data and build chunks later. - Unity: Added `SiteNotFound` and `VpsAssetsNotFound` to `Vps2LocalizationError`, matching the native site localization errors. - Python: Added `Ardk.set_frame_pacing_clock` to pace module frame rates on camera frame timestamps. - Python: Added `Vps2Session.localize_to_site` and `Vps2Session.get_asset_tracking_data`. - Python: Added a `portal_endpoint` parameter to `Ardk.create()` and `Ardk.create_with_env_config()` to choose the sites and assets endpoint. ### Changed - `stopLocalize` now cancels the site's in-flight localize requests, whose request records end `Failed` with `Unknown`. - Asset tracking data for an asset whose localize request is still fetching now reports not-yet-tracked instead of `InvalidArgument`. - When VPS stops georeferencing, the coarse output now holds the last confident VPS georeference instead of jumping to WPS. - Swift, Kotlin, Unity: Asset mesh downloads now stop at a 256 MB decoded-size budget by default; pass 0 for no budget but beware that large sites can surpass mobile device memory budgets. - Mesh downloads now decode on a background thread and free each node's response on arrival, lowering peak memory. - VPS2 area targeting now excludes every map when a trusted position is out of range of all of them, stopping localization requests. - Scanning no longer requires GPS and compass input; frames without them are recorded without location. - Unity: Playback datasets are now read through native playback instead of the managed capture.json reader. - Kotlin: Feature-session Flows such as `Vps2Session.localizationUpdates` now survive `close()`/`acquire()`, complete when `NSDKSession` closes, and emit an error only when it changes. - Swift: `GeolocationData`, `Vps2AssetTrackingData`, `Vps2LocalizedAsset`, `NSDKMapStorage`, `NSDKRecordingExporter`, and `NSDKSitesSession` now conform to `Sendable`. ### Deprecated - Swift: Deprecated `PlaybackDatasetLoader`, `BundlePlaybackDatasetLoader`, `PlaybackDatasetSource`, `captureRoot`, `compassTimestampBase`, and `PlaybackFrame.metadata`; use `PlaybackDataset.open` instead. - Kotlin: Deprecated `PlaybackDataset.loadFrom`, `AssetPlaybackDatasetLoader`, `PlaybackDatasetLoader`, `PlaybackDatasetSource`, `CaptureRoot`, and `FrameMetadata`; use `PlaybackDataset.open` instead. ### Fixed - Fixed a hang when two threads closed the same network request at once. - Fixed a possible crash when a mesh download was released while its HTTP callback was still decoding the mesh. - Fixed a crash when a feature getter ran on one thread while another thread destroyed that feature. - Fixed data races when Sites results were polled while the network response was still being written. - Fixed a scanning crash on Android devices with Samsung Xclipse 960 or newer GPUs. - Fixed playback mishandling datasets with a malformed depth resolution or missing frame fields. - Unity: Fixed the location manifest importer rejecting legacy location zips with a `LocalizationTargetId` or no top-level node. - Swift, Kotlin: Fixed `PlaybackSession` timestamps jumping backwards when playback looped to the first frame. - Swift: Fixed `PlaybackSession` playing slower than the dataset's recorded frame rate. - Swift: Fixed playback repeating edge pixels outside the image, instead of dark gray, when view and image aspect ratios differed. - Kotlin: Fixed `PlaybackSession` crashing when a frame failed to load, and running two workers after quick `pause()`/`play()` calls. - Kotlin: Fixed JNI strings corrupting characters such as emoji and aborting CheckJNI apps; strings now convert as standard UTF-8. - Kotlin: Fixed native crashes when `NSDKSession` creation failed or `SceneSegmentationConfig.numThresholds` exceeded the `thresholds` array length. - Kotlin: Fixed `createLiveDataSource` missing late precise-location grants and leaving GPS and compass running after destroy. - Kotlin: Fixed crashes when closing `NSDKSession` twice, from another thread, during an in-flight call, or with features still started. - Kotlin: Fixed the published Maven POM omitting dependencies, which caused compile errors and `NoClassDefFoundError` for Maven consumers. ## Breaking Changes - Unity: `Vps2LocalizedAsset.Equals` and `GetHashCode` now include `AreaId`, so values for different areas of the same asset no longer compare equal. Review dictionary keys and change detection that relied on site and asset IDs alone. - Unity: Playback no longer accepts `.zip` dataset paths, and the NSDK settings editor no longer rebuilds missing frame images from `video.mp4`. Unzip datasets into a directory that contains every frame image, and point the playback dataset path at that directory. - Kotlin: The NSDK AAR no longer merges `READ_EXTERNAL_STORAGE` and `WRITE_EXTERNAL_STORAGE` into apps. Apps that point `PathConfig` or log output at shared storage must declare these permissions in their own manifest. - Kotlin: `NSDKSession.close()` now throws `IllegalStateException` when called from inside an NSDK call on the same thread, including `NsdkLogCallback.onLog`, where it would otherwise deadlock. Close the session from outside NSDK callbacks. - Swift: Progress closures passed to the `NSDKRecordingExporter` export methods must now be `@Sendable`. They run off the main actor, so hop to the main actor before updating UI. `NSDKDashcamSession.saveBuffer` and `NSDKScanningSession.saveCurrentScan` are now `@MainActor`. ## Known Issues - Importing the NSDK UPM in a project started from Universal 3D template might cause a few build errors reported in the console. Restarting the editor should fix this. - VPS2 can keep reporting precise localization and tracked anchors while the device is disturbed, such as a covered camera or a dark room, so content can stay at a stale pose until confidence drops or the device localizes again. - On iPad Pro running iOS 17 in the Unity NSDK sample app, rotating the device from landscape to portrait may cause a soft lock and screen flickering.