Condividi tramite


Eseguire la migrazione dei dati di rilevamento delle versioni da un'app Xamarin.Forms a un'app MAUI .NET

Xamarin.Essentials e .NET Multipiattaforma Interfaccia utente dell'app (.NET MAUI) hanno entrambe una VersionTracking classe che consente di controllare la versione e i numeri di compilazione dell'app, oltre a informazioni aggiuntive, ad esempio se è la prima volta che è stata avviata l'app. Tuttavia, in Xamarin.Essentials i dati di rilevamento della versione vengono archiviati in un contenitore delle preferenze specifiche della piattaforma con un nome , {your-app-package-id}.xamarinessentials.versiontrackingmentre in .NET MAUI i dati di rilevamento della versione vengono archiviati in un contenitore delle preferenze specifiche della piattaforma con un nome .{your-app-package-id}.microsoft.maui.essentials.versiontracking

Quando si esegue la migrazione di un'app Xamarin.Forms che usa la VersionTracking classe a .NET MAUI, è necessario gestire questa differenza di denominazione dei contenitori delle preferenze per offrire agli utenti un'esperienza di aggiornamento uniforme. Questo articolo descrive come usare la LegacyVersionTracking classe e le classi helper per gestire il contenitore delle preferenze. La LegacyVersionTracking classe consente all'app .NET MAUI in Android, iOS e Windows di leggere i dati di rilevamento delle versioni creati con una versione precedente di Xamarin.Forms dell'app.

Importante

Affinché la classe funzioni correttamente, l'app LegacyVersionTracking .NET MAUI deve avere un numero di versione superiore al numero di versione dell'app Xamarin.Forms. Il numero di versione può essere impostato nel file di progetto dell'app .NET MAUI con le $(ApplicationVersion) proprietà di compilazione e $(ApplicationDisplayVersion) .

Per altre informazioni sulla VersionTracking classe in Xamarin.Essentials, vedere Xamarin.Essentials: Rilevamento delle versioni. Per altre informazioni sulla VersionTracking classe in .NET MAUI, vedere Rilevamento delle versioni.

Accedere ai dati di rilevamento delle versioni legacy

Il codice seguente illustra la LegacyVersionTracking classe , che fornisce l'accesso ai dati di rilevamento della versione creati dall'app Xamarin.Forms:

Nota

Per usare questo codice, aggiungerlo a una classe denominata LegacyVersionTracking nel progetto di app MAUI .NET.

namespace MigrationHelpers;

public static class LegacyVersionTracking
{
    const string versionsKey = "VersionTracking.Versions";
    const string buildsKey = "VersionTracking.Builds";

    static readonly string sharedName = LegacyPreferences.GetPrivatePreferencesSharedName("versiontracking");

    static Dictionary<string, List<string>> versionTrail;
    static string LastInstalledVersion => versionTrail[versionsKey].LastOrDefault();
    static string LastInstalledBuild => versionTrail[buildsKey].LastOrDefault();

    public static string VersionsKey => versionsKey;
    public static string BuildsKey => buildsKey;
    public static string SharedName => sharedName;
    public static bool IsFirstLaunchEver { get; private set; }
    public static bool IsFirstLaunchForCurrentVersion { get; private set; }
    public static bool IsFirstLaunchForCurrentBuild { get; private set; }
    public static string CurrentVersion => AppInfo.VersionString;
    public static string CurrentBuild => AppInfo.BuildString;
    public static string PreviousVersion => GetPrevious(versionsKey);
    public static string PreviousBuild => GetPrevious(buildsKey);
    public static string FirstInstalledVersion => versionTrail[versionsKey].FirstOrDefault();
    public static string FirstInstalledBuild => versionTrail[buildsKey].FirstOrDefault();
    public static IEnumerable<string> VersionHistory => versionTrail[versionsKey].ToArray();
    public static IEnumerable<string> BuildHistory => versionTrail[buildsKey].ToArray();
    public static bool IsFirstLaunchForVersion(string version) => CurrentVersion == version && IsFirstLaunchForCurrentVersion;
    public static bool IsFirstLaunchForBuild(string build) => CurrentBuild == build && IsFirstLaunchForCurrentBuild;

    static LegacyVersionTracking()
    {
        InitVersionTracking();
    }

    internal static void InitVersionTracking()
    {
        IsFirstLaunchEver = !LegacyPreferences.ContainsKey(versionsKey, sharedName) || !LegacyPreferences.ContainsKey(buildsKey, sharedName);
        if (IsFirstLaunchEver)
        {
            versionTrail = new Dictionary<string, List<string>>
                {
                    { versionsKey, new List<string>() },
                    { buildsKey, new List<string>() }
                };
        }
        else
        {
            versionTrail = new Dictionary<string, List<string>>
                {
                    { versionsKey, ReadHistory(versionsKey).ToList() },
                    { buildsKey, ReadHistory(buildsKey).ToList() }
                };
        }

        IsFirstLaunchForCurrentVersion = !versionTrail[versionsKey].Contains(CurrentVersion) || CurrentVersion != LastInstalledVersion;
        if (IsFirstLaunchForCurrentVersion)
        {
            // Avoid duplicates and move current version to end of list if already present
            versionTrail[versionsKey].RemoveAll(v => v == CurrentVersion);
            versionTrail[versionsKey].Add(CurrentVersion);
        }

        IsFirstLaunchForCurrentBuild = !versionTrail[buildsKey].Contains(CurrentBuild) || CurrentBuild != LastInstalledBuild;
        if (IsFirstLaunchForCurrentBuild)
        {
            // Avoid duplicates and move current build to end of list if already present
            versionTrail[buildsKey].RemoveAll(b => b == CurrentBuild);
            versionTrail[buildsKey].Add(CurrentBuild);
        }
    }

    static string GetPrevious(string key)
    {
        var trail = versionTrail[key];
        return (trail.Count >= 2) ? trail[trail.Count - 2] : null;
    }

    static string[] ReadHistory(string key) => LegacyPreferences.Get(key, null, sharedName)?.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];
}

La LegacyVersionTracking classe usa la classe , che fornisce l'accesso LegacyPreferences ai dati di rilevamento delle versioni archiviati dalla classe Xamarin.Essentials Preferences dall'app Xamarin.Forms:

Nota

Per usare questo codice, aggiungerlo a una classe denominata LegacyPreferences nel progetto di app MAUI .NET.

#if ANDROID || IOS || WINDOWS
namespace MigrationHelpers;

public static partial class LegacyPreferences
{
    internal static string GetPrivatePreferencesSharedName(string feature) => $"{AppInfo.PackageName}.xamarinessentials.{feature}";

    public static bool ContainsKey(string key, string sharedName) => PlatformContainsKey(key, sharedName);
    public static void Remove(string key, string sharedName) => PlatformRemove(key, sharedName);
    public static string Get(string key, string defaultValue, string sharedName) => PlatformGet<string>(key, defaultValue, sharedName);
}
#endif

La LegacyPreferences classe è una partial classe la cui implementazione rimanente è specifica della piattaforma.

Android

In Android la LegacyPreferences classe fornisce l'implementazione del contenitore delle preferenze che recupera i dati dalle preferenze condivise. Il codice seguente illustra la LegacyPreferences classe :

Nota

Per usare questo codice, aggiungerlo a una classe denominata LegacyPreferences nella cartella Platforms\Android del progetto di app MAUI .NET.

using System.Globalization;
using Android.Content;
using Android.Preferences;
using Application = Android.App.Application;

namespace MigrationHelpers;

public static partial class LegacyPreferences
{
    static readonly object locker = new object();

    static bool PlatformContainsKey(string key, string sharedName)
    {
        lock (locker)
        {
            using (var sharedPreferences = GetSharedPreferences(sharedName))
            {
                return sharedPreferences.Contains(key);
            }
        }
    }

    static void PlatformRemove(string key, string sharedName)
    {
        lock (locker)
        {
            using (var sharedPreferences = GetSharedPreferences(sharedName))
            using (var editor = sharedPreferences.Edit())
            {
                editor.Remove(key).Apply();
            }
        }
    }

    static T PlatformGet<T>(string key, T defaultValue, string sharedName)
    {
        lock (locker)
        {
            object value = null;
            using (var sharedPreferences = GetSharedPreferences(sharedName))
            {
                if (defaultValue == null)
                {
                    value = sharedPreferences.GetString(key, null);
                }
                else
                {
                    switch (defaultValue)
                    {
                        case int i:
                            value = sharedPreferences.GetInt(key, i);
                            break;
                        case bool b:
                            value = sharedPreferences.GetBoolean(key, b);
                            break;
                        case long l:
                            value = sharedPreferences.GetLong(key, l);
                            break;
                        case double d:
                            var savedDouble = sharedPreferences.GetString(key, null);
                            if (string.IsNullOrWhiteSpace(savedDouble))
                            {
                                value = defaultValue;
                            }
                            else
                            {
                                if (!double.TryParse(savedDouble, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out var outDouble))
                                {
                                    var maxString = Convert.ToString(double.MaxValue, CultureInfo.InvariantCulture);
                                    outDouble = savedDouble.Equals(maxString) ? double.MaxValue : double.MinValue;
                                }

                                value = outDouble;
                            }
                            break;
                        case float f:
                            value = sharedPreferences.GetFloat(key, f);
                            break;
                        case string s:
                            // the case when the string is not null
                            value = sharedPreferences.GetString(key, s);
                            break;
                    }
                }
            }

            return (T)value;
        }
    }

    static ISharedPreferences GetSharedPreferences(string sharedName)
    {
        var context = Application.Context;

        return string.IsNullOrWhiteSpace(sharedName) ?
#pragma warning disable CS0618 // Type or member is obsolete
            PreferenceManager.GetDefaultSharedPreferences(context) :
#pragma warning restore CS0618 // Type or member is obsolete
                context.GetSharedPreferences(sharedName, FileCreationMode.Private);
    }
}

iOS

In iOS la LegacyPreferences classe fornisce l'implementazione del contenitore preferenze che recupera i dati da NSUserDefaults. Il codice seguente illustra la LegacyPreferences classe :

Nota

Per usare questo codice, aggiungerlo a una classe denominata LegacyPreferences nella cartella Platforms\iOS del progetto di app MAUI .NET.

using Foundation;
using System.Globalization;

namespace MigrationHelpers;

public static partial class LegacyPreferences
{
    static readonly object locker = new object();

    static bool PlatformContainsKey(string key, string sharedName)
    {
        lock (locker)
        {
            return GetUserDefaults(sharedName)[key] != null;
        }
    }

    static void PlatformRemove(string key, string sharedName)
    {
        lock (locker)
        {
            using (var userDefaults = GetUserDefaults(sharedName))
            {
                if (userDefaults[key] != null)
                    userDefaults.RemoveObject(key);
            }
        }
    }

    static T PlatformGet<T>(string key, T defaultValue, string sharedName)
    {
        object value = null;

        lock (locker)
        {
            using (var userDefaults = GetUserDefaults(sharedName))
            {
                if (userDefaults[key] == null)
                    return defaultValue;

                switch (defaultValue)
                {
                    case int i:
                        value = (int)(nint)userDefaults.IntForKey(key);
                        break;
                    case bool b:
                        value = userDefaults.BoolForKey(key);
                        break;
                    case long l:
                        var savedLong = userDefaults.StringForKey(key);
                        value = Convert.ToInt64(savedLong, CultureInfo.InvariantCulture);
                        break;
                    case double d:
                        value = userDefaults.DoubleForKey(key);
                        break;
                    case float f:
                        value = userDefaults.FloatForKey(key);
                        break;
                    case string s:
                        // the case when the string is not null
                        value = userDefaults.StringForKey(key);
                        break;
                    default:
                        // the case when the string is null
                        if (typeof(T) == typeof(string))
                            value = userDefaults.StringForKey(key);
                        break;
                }
            }
        }

        return (T)value;
    }

    static NSUserDefaults GetUserDefaults(string sharedName)
    {
        if (!string.IsNullOrWhiteSpace(sharedName))
            return new NSUserDefaults(sharedName, NSUserDefaultsType.SuiteName);
        else
            return NSUserDefaults.StandardUserDefaults;
    }
}

Windows

In Windows la LegacyVersionTracking classe fornisce l'implementazione del contenitore preferenze che recupera i dati da ApplicationDataContainer. Il codice seguente illustra la LegacyPreferences classe :

Nota

Per usare questo codice, aggiungerlo a una classe denominata LegacyPreferences nella cartella Platforms\Windows del progetto di app MAUI .NET.

using Windows.Storage;

namespace MigrationHelpers;

public static partial class LegacyPreferences
{
    static readonly object locker = new object();

    static bool PlatformContainsKey(string key, string sharedName)
    {
        lock (locker)
        {
            var appDataContainer = GetApplicationDataContainer(sharedName);
            return appDataContainer.Values.ContainsKey(key);
        }
    }

    static void PlatformRemove(string key, string sharedName)
    {
        lock (locker)
        {
            var appDataContainer = GetApplicationDataContainer(sharedName);
            if (appDataContainer.Values.ContainsKey(key))
                appDataContainer.Values.Remove(key);
        }
    }

    static T PlatformGet<T>(string key, T defaultValue, string sharedName)
    {
        lock (locker)
        {
            var appDataContainer = GetApplicationDataContainer(sharedName);
            if (appDataContainer.Values.ContainsKey(key))
            {
                var tempValue = appDataContainer.Values[key];
                if (tempValue != null)
                    return (T)tempValue;
            }
        }

        return defaultValue;
    }

    static ApplicationDataContainer GetApplicationDataContainer(string sharedName)
    {
        var localSettings = ApplicationData.Current.LocalSettings;
        if (string.IsNullOrWhiteSpace(sharedName))
            return localSettings;

        if (!localSettings.Containers.ContainsKey(sharedName))
            localSettings.CreateContainer(sharedName, ApplicationDataCreateDisposition.Always);

        return localSettings.Containers[sharedName];
    }
}

Utilizzare i dati di rilevamento delle versioni legacy

La LegacyVersionTracking classe può essere usata per recuperare i dati di rilevamento delle versioni in Android, iOS e Windows creati con una versione precedente di Xamarin.Forms dell'app:

#if ANDROID || IOS || WINDOWS
using MigrationHelpers;
...

string isFirstLaunchEver = LegacyVersionTracking.IsFirstLaunchEver.ToString();
string currentVersionIsFirst = LegacyVersionTracking.IsFirstLaunchForCurrentVersion.ToString();
string currentBuildIsFirst = LegacyVersionTracking.IsFirstLaunchForCurrentBuild.ToString();
string currentVersion = LegacyVersionTracking.CurrentVersion.ToString();
string currentBuild = LegacyVersionTracking.CurrentBuild.ToString();
string firstInstalledVer = LegacyVersionTracking.FirstInstalledVersion.ToString();
string firstInstalledBuild = LegacyVersionTracking.FirstInstalledBuild.ToString();
string versionHistory = String.Join(',', LegacyVersionTracking.VersionHistory);
string buildHistory = String.Join(',', LegacyVersionTracking.BuildHistory);
string previousVersion = LegacyVersionTracking.PreviousVersion?.ToString() ?? "none";
string previousBuild = LegacyVersionTracking.PreviousBuild?.ToString() ?? "none";
#endif

Questo esempio mostra l'uso della LegacyVersionTracking classe per leggere i dati di rilevamento della versione legacy. Tuttavia, questi dati non possono essere assegnati alla classe MAUI di VersionTracking .NET, perché le relative proprietà non possono essere impostate. I dati possono invece essere scritti nelle preferenze MAUI .NET con il WriteHistory metodo :

void WriteHistory(string key, IEnumerable<string> history)
{
    Preferences.Default.Set(key, string.Join("|", history), $"{AppInfo.Current.PackageName}.microsoft.maui.essentials.versiontracking");
}

#if ANDROID || IOS || WINDOWS
WriteHistory(LegacyVersionTracking.VersionsKey, LegacyVersionTracking.VersionHistory);
WriteHistory(LegacyVersionTracking.BuildsKey, LegacyVersionTracking.BuildHistory);
#endif

Dopo che i dati di rilevamento delle versioni legacy sono stati scritti nelle preferenze MAUI .NET, possono essere utilizzati dalla classe MAUI di VersionTracking .NET:

var mauiVersionHistory = VersionTracking.Default.VersionHistory;
var mauiBuildHistory = VersionTracking.Default.BuildHistory;

I dati di rilevamento delle versioni legacy possono quindi essere rimossi dal dispositivo:

#if ANDROID || IOS || WINDOWS
LegacyPreferences.Remove(LegacyVersionTracking.VersionsKey, LegacyVersionTracking.SharedName);
LegacyPreferences.Remove(LegacyVersionTracking.BuildsKey, LegacyVersionTracking.SharedName);
#endif