35 lines
1.3 KiB
Dart
35 lines
1.3 KiB
Dart
import 'package:flutter/services.dart';
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
|
|
/// `errSecDuplicateItem`: the iOS keychain refused to add an item that is
|
|
/// already there.
|
|
const int kErrSecDuplicateItem = -25299;
|
|
|
|
/// Whether [error] is the iOS keychain's "item already exists" failure.
|
|
/// flutter_secure_storage surfaces it as a PlatformException whose details
|
|
/// (and message) carry the OSStatus.
|
|
bool isKeychainDuplicateItem(Object error) {
|
|
if (error is! PlatformException) return false;
|
|
if (error.details == kErrSecDuplicateItem) return true;
|
|
return error.message?.contains('$kErrSecDuplicateItem') ?? false;
|
|
}
|
|
|
|
extension KeychainWrite on FlutterSecureStorage {
|
|
/// [write] that survives `errSecDuplicateItem`. The plugin's iOS write is
|
|
/// check-then-add/update and not atomic: a concurrent write to the same key
|
|
/// — or an existence check that itself failed (seen with the disk full) —
|
|
/// ends in SecItemAdd on an existing item. The second attempt finds the
|
|
/// item and updates it.
|
|
Future<void> writeRetrying({
|
|
required String key,
|
|
required String value,
|
|
}) async {
|
|
try {
|
|
await write(key: key, value: value);
|
|
} on PlatformException catch (e) {
|
|
if (!isKeychainDuplicateItem(e)) rethrow;
|
|
await write(key: key, value: value);
|
|
}
|
|
}
|
|
}
|