Posted at

Xamarin.Authからの移行


背景

Visual Studio for Mac を 2019 にアップデートしたら Xamarin.iOS のアプリが動かなくなった。

Xamarin.Auth の AccountStore.FindAccountsForServiceAsync を呼ぶだけでエラーが起きるようになっている。

https://github.com/xamarin/Xamarin.Auth/issues/370 にissueが上がっており、対策は


  1. Xamarin.iOSのダウングレード(12.2)

  2. Xamarin.Essentials の SecureStorage への移行。

のいずれか。

前者は影響範囲が全アプリに及ぶうえに、Xamarin.Auth のこのクラスは非推奨のため、後者を選択。


やり方

Migration Guide にあるように、次のようなヘルパークラスをつくって移行すればよい。

using Newtonsoft.Json;

using Xamarin.Auth;
using Xamarin.Essentials;

public class SecureStorageAccountStore
{
public static async Task SaveAsync(Account account, string serviceId)
{
// Find existing accounts for the service
var accounts = await FindAccountsForServiceAsync(serviceId);

// Remove existing account with Id if exists
accounts.RemoveAny(a => a.Username == account.Username);

// Add account we are saving
accounts.Add(account);

// Serialize all the accounts to javascript
var json = JsonConvert.SerializeObject(accounts);

// Securely save the accounts for the given service
await SecureStorage.SetAsync(serviceId, json);
}

public static async Task<List<Account>> FindAccountsForServiceAsync(string serviceId)
{
// Get the json for accounts for the service
var json = await SecureStorage.GetAsync(serviceId);

try {
// Try to return deserialized list of accounts
return JsonConvert.DeserializeObject<List<Account>>(json);
} catch { }

// If this fails, return an empty list
return new List<Account>();
}

public static async Task MigrateAllAccountsAsync(string serviceId, IEnumerable<Account> accountStoreAccounts)
{
var wasMigrated = await SecureStorage.GetAsync("XamarinAuthAccountStoreMigrated", "0");

if (wasMigrated == "1")
return;

await SecureStorage.SetAsync("XamarinAuthAccountStoreMigrated", "1");

// Just in case, look at existing 'new' accounts
var accounts = await FindAccountsForServiceAsync(serviceId);

foreach (var account in accountStoreAccounts) {

// Check if the new storage already has this account
// We don't want to overwrite it if it does
if (accounts.Any(a => a.Username == account.Username))
continue;

// Add the account we are migrating
accounts.Add(account);
}

// Serialize all the accounts to javascript
var json = JsonConvert.SerializeObject(accounts);

// Securely save the accounts for the given service
await SecureStorage.SetAsync(serviceId, json);
}
}

※ 上は挙げられているコードそのままだが、実際には System.Linq などの import をたす必要あり。


問題

が、結局この前後のデータのマイグレーションにも Find... が必要なため、この事態が起きてしまうということ。許容できない場合は Xamarin.Native でキーチェーンにアクセスして取り出す処理を書く必要がある。

もう一点、iOS SDK の問題だが、シミュレータ上では Entitlements.plist の編集が必要になる。通常(実機で使う場合)は不要のため、地味にストレスになる。

https://docs.microsoft.com/ja-jp/xamarin/essentials/secure-storage?context=xamarin%2Fandroid&tabs=ios