refactor(concurrency): isolate ProviderConfig to the main actor and drop unneeded @unchecked Sendable

- ProviderConfig is now @MainActor instead of @unchecked Sendable; AppL10n reads the persisted UI language key directly so off-main string lookups stay valid.
- EnglishLearningStore serializes its read-modify-write with a type-wide lock (several instances share one defaults key).
- URLSession-only transports, GatewayGrantKeychainStore and Keychain conform to Sendable without @unchecked. UserDefaults/DeviceCheck wrappers keep @unchecked because those SDK types are not Sendable.
This commit is contained in:
2026-10-20 23:00:12 +02:00
parent 9a4107cef8
commit d6b614b8be
13 changed files with 38 additions and 14 deletions
@@ -12,7 +12,7 @@ protocol PublicContentHTTPTransport: Sendable {
final class URLSessionPublicContentHTTPTransport:
PublicContentHTTPTransport,
@unchecked Sendable {
Sendable {
private let session: URLSession
init(session: URLSession = .shared) {
+9 -2
View File
@@ -30,9 +30,16 @@ enum AppL10n {
/// screen that exists so a provisioning mistake does *not* become a crash
/// loop. `.auto` follows the system language, which is the right answer
/// when no stored preference is reachable anyway.
///
/// Reads the persisted key directly rather than `ProviderConfig.shared`
/// (main-actor isolated) because localized strings are also resolved off
/// the main actor. `ProviderConfig` writes this key synchronously in its
/// `uiLanguage` didSet, so both paths observe the same value.
private static var resolvedUILanguage: AppUILanguage {
guard AppGroup.isAvailable else { return .auto }
return ProviderConfig.shared.uiLanguage
guard let defaults = AppGroup.defaultsIfAvailable else { return .auto }
return AppUILanguage.fromStored(
defaults.string(forKey: AppGroupConfiguration.Keys.uiLanguage)
)
}
static func format(
@@ -91,6 +91,7 @@ struct SettingsVersionRow: View {
// MARK: - Config entry summaries (shown on Settings home)
@MainActor
enum SettingsConfigSummary {
static func speechRecognition(config: ProviderConfig) -> String {
if config.engineMode == "local" {