Перенос данных отслеживания версий из приложения Xamarin.Forms в приложение .NET MAUI
Xamarin.Essentials и многоплатформенный пользовательский интерфейс приложений .NET (.NET MAUI) имеют VersionTracking
класс, который позволяет проверка версии и номера сборки приложения, а также дополнительные сведения, например, если приложение было запущено в первый раз. Однако в Xamarin.Essentials данные отслеживания версий хранятся в контейнере параметров для конкретной платформы с именем, а в .NET MAUI данные отслеживания версий хранятся в контейнере параметров для конкретной платформы с именем{your-app-package-id}.xamarinessentials.versiontracking
{your-app-package-id}.microsoft.maui.essentials.versiontracking
.
При переносе приложения Xamarin.Forms, использующего класс в VersionTracking
.NET MAUI, необходимо иметь дело с этим различием именования контейнеров, чтобы предоставить пользователям возможность плавного обновления. В этой статье описывается, как использовать LegacyVersionTracking
класс и вспомогательные классы для работы с контейнером параметров. Класс LegacyVersionTracking
позволяет приложению .NET MAUI в Android, iOS и Windows читать данные отслеживания версий, созданные с предыдущей версией приложения Xamarin.Forms.
Внимание
LegacyVersionTracking
Чтобы класс работал правильно, приложение MAUI .NET должно иметь более высокий номер версии, чем номер версии приложения Xamarin.Forms. Номер версии можно задать в файле проекта приложения .NET MAUI с $(ApplicationVersion)
помощью свойств сборки и $(ApplicationDisplayVersion)
свойств сборки.
Дополнительные сведения о VersionTracking
классе в Xamarin.Essentials см. в разделе Xamarin.Essentials: отслеживание версий. Дополнительные сведения о VersionTracking классе в .NET MAUI см. в разделе "Отслеживание версий".
Доступ к устаревшим данным отслеживания версий
В следующем коде LegacyVersionTracking
показан класс, который предоставляет доступ к данным отслеживания версий, созданным приложением Xamarin.Forms:
Примечание.
Чтобы использовать этот код, добавьте его в класс с именем LegacyVersionTracking
в проекте приложения .NET MAUI.
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];
}
Класс LegacyVersionTracking
использует LegacyPreferences
класс, который предоставляет доступ к данным отслеживания версий, хранящимся в классе Xamarin.Essentials Preferences
из приложения Xamarin.Forms:
Примечание.
Чтобы использовать этот код, добавьте его в класс с именем LegacyPreferences
в проекте приложения .NET MAUI.
#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
Класс LegacyPreferences
— это partial
класс, оставшаяся реализация которого зависит от платформы.
Android
В LegacyPreferences
Android класс предоставляет реализацию контейнера параметров, извлекающую данные из общих предпочтений. В следующем коде демонстрируется класс LegacyPreferences
:
Примечание.
Чтобы использовать этот код, добавьте его в класс с именем LegacyPreferences
в папке Platform\Android проекта приложения .NET MAUI.
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
В iOS LegacyPreferences
класс предоставляет реализацию контейнера параметров, из которого извлекаются данные NSUserDefaults
. В следующем коде демонстрируется класс LegacyPreferences
:
Примечание.
Чтобы использовать этот код, добавьте его в класс с именем LegacyPreferences
в папке Platform\iOS проекта приложения .NET MAUI.
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
В LegacyVersionTracking
Windows класс предоставляет реализацию контейнера параметров, из которого извлекаются данные ApplicationDataContainer. В следующем коде демонстрируется класс LegacyPreferences
:
Примечание.
Чтобы использовать этот код, добавьте его в класс с именем LegacyPreferences
в папке Platform\Windows проекта приложения .NET MAUI.
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];
}
}
Использование устаревших данных отслеживания версий
Класс LegacyVersionTracking
можно использовать для получения данных отслеживания версий в Android, iOS и Windows, созданных с помощью предыдущей версии приложения Xamarin.Forms:
#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
В этом примере показано использование LegacyVersionTracking
класса для чтения устаревших данных отслеживания версий. Однако эти данные не могут быть назначены классу .NET MAUI VersionTracking , так как его свойства не могут быть заданы. Вместо этого данные можно записать в настройки .NET MAUI с WriteHistory
помощью метода:
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
После записи устаревших данных отслеживания версий в настройки .NET MAUI его можно использовать классом .NET MAUI VersionTracking :
var mauiVersionHistory = VersionTracking.Default.VersionHistory;
var mauiBuildHistory = VersionTracking.Default.BuildHistory;
После этого данные отслеживания устаревших версий можно удалить с устройства:
#if ANDROID || IOS || WINDOWS
LegacyPreferences.Remove(LegacyVersionTracking.VersionsKey, LegacyVersionTracking.SharedName);
LegacyPreferences.Remove(LegacyVersionTracking.BuildsKey, LegacyVersionTracking.SharedName);
#endif