Skip to content

Instantly share code, notes, and snippets.

@DanMillerDev
Created March 18, 2025 00:27
Show Gist options
  • Save DanMillerDev/22e1b1b9a4348bfecdb7416dabaa69f0 to your computer and use it in GitHub Desktop.
Save DanMillerDev/22e1b1b9a4348bfecdb7416dabaa69f0 to your computer and use it in GitHub Desktop.
Saves and loads SerializableGuids and DateTimes to a file on the devices local storage for persistent anchors
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.XR.ARSubsystems;
/// <summary>
/// Saves and loads SerializableGuids and DateTimes to a file on the devices local storage.
/// </summary>
public class AnchorSaveFile
{
readonly string m_FilePath = Path.Combine(Application.persistentDataPath, "SavedAnchorIds.json");
bool m_Initialized;
Awaitable m_InitializeAwaitable;
Dictionary<SerializableGuid, DateTime> m_SavedAnchorsData = new();
static AnchorSaveFile s_Instance;
public static AnchorSaveFile instance
{
get
{
if (s_Instance != null)
return s_Instance;
s_Instance = new AnchorSaveFile();
return s_Instance;
}
}
AnchorSaveFile()
{
m_InitializeAwaitable = PopulateSavedAnchorIdsFromFile();
}
public Awaitable SaveAnchorIdAsync(SerializableGuid savedAnchorId)
{
return SaveAnchorIdAsync(savedAnchorId, DateTime.Now);
}
/// <summary>
/// Saves a `SerializableGuid` to a file asynchronously, appending to the list of ids already saved.
/// If no file exists or the file is unreadable, a new file is created.
/// </summary>
/// <param name="savedAnchorId">The `SerializableGuid` to save.</param>
public async Awaitable SaveAnchorIdAsync(SerializableGuid savedAnchorId, DateTime dateTime)
{
try
{
if (!m_Initialized)
await m_InitializeAwaitable;
if (!m_SavedAnchorsData.TryAdd(savedAnchorId, dateTime))
m_SavedAnchorsData[savedAnchorId] = dateTime;
await WriteSavedAnchorIdsToFile();
}
catch (Exception e)
{
Debug.LogException(e);
}
}
public async Awaitable EraseAnchorIdAsync(SerializableGuid savedAnchorId)
{
try
{
if (!m_Initialized)
await m_InitializeAwaitable;
m_SavedAnchorsData.Remove(savedAnchorId);
await WriteSavedAnchorIdsToFile();
}
catch (Exception e)
{
Debug.LogException(e);
}
}
/// <summary>
/// Returns the set of `SerializableGuid`s from the save file.
/// </summary>
/// <returns>The set of `SerializableGuid`s that were saved to the file.
/// If no file exists or the file is unreadable, an an empty set is returned.</returns>
public async Awaitable<Dictionary<SerializableGuid, DateTime>> GetSavedAnchorsDataAsync()
{
if (!m_Initialized)
await m_InitializeAwaitable;
return m_SavedAnchorsData;
}
async Awaitable PopulateSavedAnchorIdsFromFile()
{
try
{
m_SavedAnchorsData.Clear();
if (!File.Exists(m_FilePath))
return;
using var streamReader = File.OpenText(m_FilePath);
using var jsonTextReader = new JsonTextReader(streamReader);
var kvp = (JObject)await JToken.ReadFromAsync(jsonTextReader);
foreach (var (idAsString, dateTime) in kvp)
{
var tokens = idAsString.Split("-");
var low = Convert.ToUInt64(tokens[0], 16);
var high = Convert.ToUInt64(tokens[1], 16);
var serializableGuid = new SerializableGuid(low, high);
m_SavedAnchorsData.Add(serializableGuid, (DateTime)dateTime);
}
}
catch (Exception e)
{
Debug.LogException(e);
}
finally
{
m_Initialized = true;
}
}
async Awaitable WriteSavedAnchorIdsToFile()
{
var jsonString = JsonConvert.SerializeObject(m_SavedAnchorsData, Formatting.Indented);
await File.WriteAllTextAsync(m_FilePath, jsonString);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment