Xamarin.Forms アプリから .NET MAUI アプリにバージョン追跡データを移行する
Xamarin.Essentials と .NET Multi-platform App UI (.NET MAUI) にはどちらも VersionTracking
クラスがあり、アプリのバージョンとビルド番号、およびアプリが初めて起動されたかどうかなどの追加情報を確認できます。 ただし、Xamarin.Essentials では、バージョン追跡データは {your-app-package-id}.xamarinessentials.versiontracking
という名前のプラットフォーム固有のユーザー設定コンテナーに保存されますが、.NET MAUI では、バージョン追跡データは {your-app-package-id}.microsoft.maui.essentials.versiontracking
という名前のプラットフォーム固有のユーザー設定コンテナーに保存されます。
VersionTracking
クラスを使用する Xamarin.Forms アプリを .NET MAUI に移行する場合は、ユーザーにスムーズなアップグレード エクスペリエンスを提供するために、このユーザー設定コンテナーの名前の違いに対処する必要があります。 この記事では、LegacyVersionTracking
クラスとヘルパー クラスを使用してユーザー設定コンテナーを処理する方法について説明します。 LegacyVersionTracking
クラスを使用すると、Android、iOS、Windows 上の .NET MAUI アプリで、アプリの以前の Xamarin.Forms バージョンで作成されたバージョン追跡データを読み取ることができます。
重要
LegacyVersionTracking
クラスが正しく動作するには、.NET MAUI アプリのバージョン番号が Xamarin.Forms アプリのバージョン番号よりも大きい必要があります。 バージョン番号は、$(ApplicationVersion)
および $(ApplicationDisplayVersion)
のビルド プロパティを使用して .NET MAUI アプリのプロジェクト ファイルに設定できます。
Xamarin.Essentials の VersionTracking
クラスの詳細については、「Xamarin.Essentials: バージョン追跡」を参照してください。 .NET MAUI の VersionTracking クラスの詳細については、「バージョン追跡」を参照してください。
従来のバージョン追跡データへのアクセス
次のコードは、Xamarin.Forms アプリによって作成されたバージョン追跡データへのアクセスを提供する LegacyVersionTracking
クラスを示しています。
Note
このコードを使用するには、.NET MAUI アプリ プロジェクトの LegacyVersionTracking
という名前のクラスにコードを追加します。
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.Forms アプリから Xamarin.Essentials Preferences
クラスによって保存されたバージョン追跡データへのアクセスが提供されます。
Note
このコードを使用するには、.NET MAUI アプリ プロジェクトの LegacyPreferences
という名前のクラスにこのコードを追加します。
#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
Android では、LegacyPreferences
クラスは、共有設定からデータを取得してユーザー設定コンテナーを実装します。 次のコードは LegacyPreferences
クラスを示しています。
Note
このコードを使用するには、.NET MAUI アプリ プロジェクトの Platforms\Android フォルダーにある LegacyPreferences
という名前のクラスにこのコードを追加します。
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
クラスを示しています。
Note
このコードを使用するには、.NET MAUI アプリ プロジェクトの Platforms\iOS フォルダーにある LegacyPreferences
という名前のクラスにこのコードを追加します。
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
Windows では、LegacyVersionTracking
クラスは、ApplicationDataContainer からデータを取得してユーザー設定コンテナーを実装します。 次のコードは LegacyPreferences
クラスを示しています。
Note
このコードを使用するには、.NET MAUI アプリ プロジェクトの Platforms\Windows フォルダーにある LegacyPreferences
という名前のクラスにこのコードを追加します。
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
クラスを使用すると、アプリの以前の Xamarin.Forms バージョンで作成された Android、iOS、Windows 上のバージョン追跡データを取得できます。
#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 クラスに割り当てることはできません。 代わりに、WriteHistory
メソッドを使用してデータを .NET MAUI 設定に書き込むことができます。
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
.NET MAUI