.netmaui

How to access application properties in .NET MAUI?


I'm migrating an Android/iOS Xamarin.Forms application to .NET MAUI. In old version of the application I used Application.Properties to store some information. Now I can't use it, becuase it is marked with ObsoleteAttribute with IsError property set to true, so I get compilation error. I get an information, that I should use Microsoft.Maui.Storage.Preferences instead and that's what I did. The problem is, I have to move Properties to Preferences somehow after the application update. And I have no idea how I can access those properties. I was trying to ignore ObsoleteAttribute, but solutions like adding #pragma warning disable directive work only with warnings, not with compilation errors. Also I tried to access those properties via reflection, but I get NotSupportedException with message "Properties API is obsolete, use Microsoft.Maui.Storage.Preferences instead". Is there any way to disable this compilation error, or access those properties in some other way? Thank you for all ideas in advance.


Solution

  • For those of you, who still need this. I'll describe some essential parts of official Microsoft guide described in this article: https://learn.microsoft.com/en-us/dotnet/maui/migration/app-properties?view=net-maui-8.0 .

    Create a LegacyApplication class:

    public class LegacyApplication
    {
        readonly PropertiesDeserializer deserializer;
        Task<IDictionary<string, object>>? propertiesTask;
    
        static LegacyApplication? current;
        public static LegacyApplication? Current
        {
            get
            {
                current ??= (LegacyApplication)Activator.CreateInstance(typeof(LegacyApplication));
                return current;
            }
        }
    
        public LegacyApplication()
        {
            deserializer = new PropertiesDeserializer();
        }
    
        public IDictionary<string, object> Properties
        {
            get
            {
                propertiesTask ??= GetPropertiesAsync();
                return propertiesTask.Result;
            }
        }
    
        async Task<IDictionary<string, object>> GetPropertiesAsync()
        {
            IDictionary<string, object> properties = await deserializer.DeserializePropertiesAsync().ConfigureAwait(false);
            properties ??= new Dictionary<string, object>(4);
            return properties;
        }
    }
    

    As you can see it uses PropertiesDeserializer, that should have a different implementation for iOS, Android and Windows.

    iOS

    public class PropertiesDeserializer
    {
        const string PropertyStoreFile = "PropertyStore.forms";
    
        public Task<IDictionary<string, object>> DeserializePropertiesAsync()
        {
            // Deserialize property dictionary to local storage
            return Task.Run(() =>
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                using (var stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.OpenOrCreate))
                using (var reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
                {
                    if (stream.Length == 0)
                        return null;
    
                    try
                    {
                        var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
                        return (IDictionary<string, object>)dcs.ReadObject(reader);
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine("Could not deserialize properties: " + e.Message);
                        Console.WriteLine($"PropertyStore Exception while reading Application properties: {e}");
                    }
                }
                return null;
            });
        }
    }
    

    Android

    public class PropertiesDeserializer
    {
        const string PropertyStoreFile = "PropertyStore.forms";
    
        public Task<IDictionary<string, object>> DeserializePropertiesAsync()
        {
            // Deserialize property dictionary to local storage
            return Task.Run(() =>
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (!store.FileExists(PropertyStoreFile))
                        return null;
    
                    using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile, FileMode.Open, FileAccess.Read))
                    using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
                    {
                        if (stream.Length == 0)
                            return null;
    
                        try
                        {
                            var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
                            return (IDictionary<string, object>)dcs.ReadObject(reader);
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine("Could not deserialize properties: " + e.Message);
                            Console.WriteLine($"PropertyStore Exception while reading Application properties: {e}");
                        }
                    }
                }
                return null;
            });
        }
    }
    

    Windows

    public class PropertiesDeserializer
    {
        const string PropertyStoreFile = "PropertyStore.forms";
    
        public async Task<IDictionary<string, object>> DeserializePropertiesAsync()
        {
            try
            {
                StorageFile file = await ApplicationData.Current.RoamingFolder.GetFileAsync(PropertyStoreFile).DontSync();
                using (Stream stream = (await file.OpenReadAsync().DontSync()).AsStreamForRead())
                {
                    if (stream.Length == 0)
                        return new Dictionary<string, object>(4);
    
                    try
                    {
                        var serializer = new DataContractSerializer(typeof(IDictionary<string, object>));
                        return (IDictionary<string, object>)serializer.ReadObject(stream);
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine("Could not deserialize properties: " + e.Message);
                        Console.WriteLine($"PropertyStore Exception while reading Application properties: {e}");
                    }
                    return null;
                }
            }
            catch (FileNotFoundException)
            {
                return new Dictionary<string, object>(4);
            }
        }
    }
    

    Now you can finally consume the properties with LegacyApplication.Current.Properties and rewrite them to Maui preferences.