Isolated storage is deleted when updating my game

Hello!

When I publish a new version of my game on Windows (OpenGL, UWP) using Right click → Publish → Create app package and upload it to the store, as soon as I update the already installed game from store, the isolated storage is deleted (or maybe it changes the location?)

This is not happening on Android (Archive → Distribute to Google Play)

Any ideas?

I am using Isolated storage for storing my save files.

Instead of
IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);

I will try
IsolatedStorageFile.GetUserStoreForApplication();

and see.

I think when I publish the game, assembly version changes atd it is causing this mess

Guys, I really need help :slight_smile:

GetUserStoreForApplication did not work either

Is there any cross platform library which I can use to simply store and retrieve text files? Currently I am using my own class - nazdar-the-game/FileIO.cs at master · MichalSkoula/nazdar-the-game · GitHub

I don’t know how IsolatedStorageFile works. I think writing to the actual file system for mobile platforms is not desired anymore and that using some kind of cloud storage is preferred, but I don’t know how to use that.

For my own game, supporting Windows and Android, I used file storage as well and used the nuget package PCLStorage.

To use this, I made an interface…

namespace ArbitraryPixel.Common.SimpleFileSystem
{
    /// <summary>
    /// Represents an object that can perform simple interactions with a file system.
    /// </summary>
    public interface ISimpleFileSystem
    {
        /// <summary>
        /// Check whether or not a file exists.
        /// </summary>
        /// <param name="fileName">The file to check.</param>
        /// <returns>True if the file exists on the file system, false otherwise.</returns>
        bool FileExists(string fileName);

        /// <summary>
        /// Check whether or not a folder exists.
        /// </summary>
        /// <param name="folderName">The folder to check.</param>
        /// <returns>True if the folder exists on the file system, false otherwise.</returns>
        bool FolderExists(string folderName);

        /// <summary>
        /// Create a folder in the file system. An exception will be thrown if the folder already exists.
        /// </summary>
        /// <param name="folderName">The name of the folder to create.</param>
        void CreateFolder(string folderName);

        /// <summary>
        /// Read the contents of a file.
        /// </summary>
        /// <param name="fileName">The file to read.</param>
        /// <returns>A string containing the contents of the file.</returns>
        string ReadFileContents(string fileName);

        /// <summary>
        /// Write a string to a file, overwriting the file if it exists.
        /// </summary>
        /// <param name="fileName">The file to write to.</param>
        /// <param name="contents">The string to write to the file.</param>
        void WriteFileContents(string fileName, string contents);

        /// <summary>
        /// Delete a file.
        /// </summary>
        /// <param name="fileName">The file to delete.</param>
        void DeleteFile(string fileName);
    }
}

… and then an implementation.

using PCLStorage;
using System.Threading.Tasks;

namespace ArbitraryPixel.Common.SimpleFileSystem.PCLPortable
{
    public class PCLStorageFileSystem : ISimpleFileSystem
    {
        public void DeleteFile(string fileName)
        {
            var t = Task.Run(
                async () =>
                {
                    var root = FileSystem.Current.LocalStorage;
                    var file = await root.GetFileAsync(fileName);
                    await file.DeleteAsync();
                }
            );
            t.Wait();
        }

        public bool FileExists(string fileName)
        {
            var t = Task<bool>.Run(
                async () =>
                {
                    var root = FileSystem.Current.LocalStorage;
                    var result = await root.CheckExistsAsync(fileName);

                    return (result == ExistenceCheckResult.FileExists);
                }
            );
            t.Wait();

            return t.Result;
        }

        public bool FolderExists(string folderName)
        {
            var t = Task<bool>.Run(
                async () =>
                {
                    var root = FileSystem.Current.LocalStorage;
                    var result = await root.CheckExistsAsync(folderName);

                    return (result == ExistenceCheckResult.FolderExists);
                }
            );
            t.Wait();

            return t.Result;
        }

        public string ReadFileContents(string fileName)
        {
            var t = Task<string>.Run(
                async () =>
                {
                    var root = FileSystem.Current.LocalStorage;
                    var file = await root.GetFileAsync(fileName);
                    return await file.ReadAllTextAsync();
                }
            );
            t.Wait();

            return t.Result;
        }

        public void WriteFileContents(string fileName, string contents)
        {
            var t = Task.Run(
                async () =>
                {
                    var root = FileSystem.Current.LocalStorage;
                    var file = await root.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
                    await file.WriteAllTextAsync(contents);
                }
            );
            t.Wait();
        }

        public void CreateFolder(string folderName)
        {
            var t = Task.Run(
                async () =>
                {
                    var root = FileSystem.Current.LocalStorage;
                    await root.CreateFolderAsync(folderName, CreationCollisionOption.FailIfExists);
                }
            );
            t.Wait();
        }
    }
}

For the locations I wrote to, I used…
Windows:

_appStorageDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "ArbitraryPixel", "CodeLogic");

Android:

_appStorageDir = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.ToString(), "ArbitraryPixel", "CodeLogic");

For Android in particular, you have to make sure you actually have storage permissions, so you can check this and then request it if you want to with this code…

        #region Permission Check Methods
        private const int REQUESTID_PERMISSION_STORAGE = 1;
        private bool CheckStoragePermissions()
        {
            bool hasStoragePermissions = true
                && ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) == Permission.Granted
                && ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) == Permission.Granted
                && true;

            return hasStoragePermissions;
        }

        private void RequestRequiredPermissions(bool hasStoragePermissions)
        {
            if (!hasStoragePermissions)
                ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.WriteExternalStorage, Manifest.Permission.ReadExternalStorage }, REQUESTID_PERMISSION_STORAGE);
        }
        #endregion

Don’t worry about the extra true in the hasStoragePermissions check, it was just a formatting style I was experimenting with at the time :wink:

Which I got from somewhere in the Android API documentation in 2018 when I wrote this. So there’s a real chance this might be somewhat out of date, but it’s what worked for me. I then just wrote and read json files for my configuration and data.

I hope that helps!

1 Like

Wow, thank you for that example and explanation. I will take a look!

1 Like

In the end, I decided to remove IsolatedStorage and utilize preprocessor directives, allowing me to easily work with files and directories in special folders through System.IO.File and System.IO.Directory.

The solution is incredibly simple, yet I couldn’t find it anywhere on the internet.

As you can see it works with Android, UWP and also OpenGL.

It doesn’t even require storage permissions.

#if __ANDROID__
            this.Folder = GetFolderPath(SpecialFolder.ApplicationData);
#elif NETFX_CORE
            this.Folder = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
#else
            this.Folder = Path.Combine(GetFolderPath(SpecialFolder.ApplicationData), "NazdarTheGame");
#endif

(NETFX_CORE = UWP)

I’m surprised by this, though perhaps that folder is less restricted than the one I ended up writing to? Either way, I’m glad you were able to get it working :slight_smile:

Side note, if a bunch of #if/#endif stuff bothers you (as it does for me), I recommend using an interface to define behaviour and an implementation per platform to give you the values you want. I won’t go into details here though since that’s not really on topic.

Anyway, way to go!

1 Like

I just found out that one of my apps has this problem. It’s an educational app, which maintains a history of what the user has completed. My daughter just downloaded an update and her history is now empty. :frowning:

@MikeS Do you know if your approach works with iOS? Your example only includes Android, UWP and OpenGL. Appreciate any insights you can provide.

sadly I dont have any Apply product - I was testing it only with these 3 platforms

Here is what I ended up using for iOS:

var storagePath = Path.Combine(GetFolderPath(SpecialFolder.MyDocuments), "IsolatedStorage");

My use case was complicated since I already had 2 versions on the App Store (and complaints from v2 users that their data disappeared on upgrade). I had to change my load logic to iterate through all existing folders and files under storagePath and merge their contents into a single file. I have other apps on the App Store with the same issue, and will deal with them if and when I publish a new version.

1 Like