背景
Visual Studio for Mac を 2019 にアップデートしたら Xamarin.iOS のアプリが動かなくなった。
Xamarin.Auth の AccountStore.FindAccountsForServiceAsync
を呼ぶだけでエラーが起きるようになっている。
https://github.com/xamarin/Xamarin.Auth/issues/370 にissueが上がっており、対策は
- Xamarin.iOSのダウングレード(12.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 の編集が必要になる。通常(実機で使う場合)は不要のため、地味にストレスになる。