Reputation: 365
I'm building an app that needs to save and load user created data to the persistent data path. When I play it in the Unity Editor it works perfectly, but when I try it on iOS, I get this error:
Uploading Crash Report
NullReferenceException: Object reference not set to an instance of an object.
at CustomModeScript.LoadSavedModes () [0x00000] in <00000000000000000000000000000000>:0
at CustomModeScript.Initialize (System.String fromPanel) [0x00000] in <00000000000000000000000000000000>:0
at MainSettingsPanelScript.OnControlModes () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.Events.UnityAction.Invoke () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.Events.UnityEvent.Invoke () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1].Invoke (T1 handler, UnityEngine.EventSystems.BaseEventData eventData) [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchPress (UnityEngine.EventSystems.PointerEventData pointerEvent, System.Boolean pressed, System.Boolean released) [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchEvents () [0x00000] in <00000000000000000000000000000000>:0
at UnityEngine.EventSystems.StandaloneInputModule.Process () [0x00000] in <00000000000000000000000000000000>:0
UnityEngine.EventSystems.ExecuteEvents:Execute(GameObject, BaseEventData, EventFunction`1)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchPress(PointerEventData, Boolean, Boolean)
UnityEngine.EventSystems.StandaloneInputModule:ProcessTouchEvents()
UnityEngine.EventSystems.StandaloneInputModule:Process()
It used to be an ioexception: Sharing violation on path error, but now it's the error above after I refactored my Load function. The issue is currently happening when I try to load the data (I can't really tell if it is saving or not)
Here is the functions for Save and Load (Retrieve)
public void SaveModes()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/UserCustomModes.jl");
List<Mode> data = new List<Mode>();
data = savedModes;
bf.Serialize(file , data);
file.Close();
}
// Retrieve saved modes if they exist
public List<Mode> RetrieveSavedModes()
{
if(File.Exists(Application.persistentDataPath + "/UserCustomModes.jl"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/UserCustomModes.jl", FileMode.Open);
List<Mode> storedModesFile = bf.Deserialize(file) as List<Mode>;
savedModes = storedModesFile;
file.Close();
}
return savedModes;
}
Here is the code that is calling the load function:
// Populate mode view with saved modes
private void PopulateModeView()
{
ClearCustomModes();
Debug.Log("Loading Modes: " + Manager.savedModes.Count);
if (Manager.savedModes.Count > 0)
{
//Debug.Log("More than 0 modes present");
var index = 0;
foreach (var savedMode in Manager.savedModes)
{
var modeItem = Instantiate(customModeItemPrefab, customModeSectionTransform);
modeItem.GetComponent<CustomModeItemScript>().Initialize(savedMode, index, this, mainSettingsPanelScript);
generatedModeButtons.Add(modeItem);
}
}
}
Here is the LoadSavedModes Function:
private void LoadSavedModes()
{
RemoveAllModeButtons();
Manager.savedModes = Manager.RetrieveSavedModes();
Debug.Log("[UNITY] Retrieve Modes Count: " + Manager.savedModes.Count);
PopulateModeView();
}
Thank you in advance!
Upvotes: 3
Views: 14299
Reputation: 6275
Here is my personal templated Save/Load
script. I know it works for all platforms currently listed in the function GetFilePath
.
using UnityEngine;
using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Collections.Generic;
/// <summary>
/// Saves, loads and deletes all data in the game
/// </summary>
/// <typeparam name="T"></typeparam>
public static class SaveLoad<T>
{
/// <summary>
/// Save data to a file (overwrite completely)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="folder"></param>
/// <param name="file"></param>
public static void Save(T data, string folder, string file)
{
// get the data path of this save data
string dataPath = GetFilePath(folder, file);
string jsonData = JsonUtility.ToJson(data, true);
byte[] byteData;
byteData = Encoding.ASCII.GetBytes(jsonData);
// create the file in the path if it doesn't exist
// if the file path or name does not exist, return the default SO
if (!Directory.Exists(Path.GetDirectoryName(dataPath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(dataPath));
}
// attempt to save here data
try
{
// save datahere
File.WriteAllBytes(dataPath, byteData);
Debug.Log("Save data to: " + dataPath);
}
catch (Exception e)
{
// write out error here
Debug.LogError("Failed to save data to: " + dataPath);
Debug.LogError("Error " + e.Message);
}
}
/// <summary>
/// Load all data at a specified file and folder location
/// </summary>
/// <param name="folder"></param>
/// <param name="file"></param>
/// <returns></returns>
public static T Load(string folder, string file)
{
// get the data path of this save data
string dataPath = GetFilePath(folder, file);
// if the file path or name does not exist, return the default SO
if (!Directory.Exists(Path.GetDirectoryName(dataPath)))
{
Debug.LogWarning("File or path does not exist! " + dataPath);
return default(T);
}
// load in the save data as byte array
byte[] jsonDataAsBytes = null;
try
{
jsonDataAsBytes = File.ReadAllBytes(dataPath);
Debug.Log("<color=green>Loaded all data from: </color>" + dataPath);
}
catch (Exception e)
{
Debug.LogWarning("Failed to load data from: " + dataPath);
Debug.LogWarning("Error: " + e.Message);
return default(T);
}
if (jsonDataAsBytes == null)
return default(T);
// convert the byte array to json
string jsonData;
// convert the byte array to json
jsonData = Encoding.ASCII.GetString(jsonDataAsBytes);
// convert to the specified object type
T returnedData = JsonUtility.FromJson<T>(jsonData);
// return the casted json object to use
return (T)Convert.ChangeType(returnedData, typeof(T));
}
/// <summary>
/// Create file path for where a file is stored on the specific platform given a folder name and file name
/// </summary>
/// <param name="FolderName"></param>
/// <param name="FileName"></param>
/// <returns></returns>
private static string GetFilePath(string FolderName, string FileName = "")
{
string filePath;
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
// mac
filePath = Path.Combine(Application.streamingAssetsPath, ("data/" + FolderName));
if (FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
// windows
filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));
if(FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_ANDROID
// android
filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));
if(FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#elif UNITY_IOS
// ios
filePath = Path.Combine(Application.persistentDataPath, ("data/" + FolderName));
if(FileName != "")
filePath = Path.Combine(filePath, (FileName + ".txt"));
#endif
return filePath;
}
}
Here is an example to use the script
[System.Serializable]
public class TestClass
{
public TestClass()
{
testData = new List<int>();
}
public List<int> testData = new List<int>();
}
public class TestMono : MonoBehaviour
{
private string testFolder = "folder";
private string testFile = "file";
private List<int> myList = new List<int>();
private void Awake()
{
// the ?? is a null comparison so if the value returns null, it generates new data
TestClass loadedData = SaveLoad<TestClass>.Load(folder, file) ?? new TestClass();
myList = loadedData.testData;
}
private void SaveData()
{
TestClass dataToSave = new TestClass
{
testData = myList
};
SaveLoad<TestClass>.Save(dataToSave, folder, file);
}
}
The above snippet is untested but is the general use-case for the Save/Load
script.
Upvotes: 4