Files
yattee/Yattee/Services/Credentials/AccountCredentialsManager.swift
Arkadiusz Fal 4770bbe3b9 Remove dead code found by Periphery on all platforms
Delete declarations that Periphery reports unused on every platform that
compiles them (iOS, macOS, tvOS), including nine views used only by their
own previews. Platform-gated code still used on another platform, device-
only MPV render paths, migration models, CloudKit record names, App
Intents metadata and unused parameters are kept.
2026-10-08 23:57:56 +02:00

103 lines
4.8 KiB
Swift

//
// AccountCredentialsManager.swift
// Yattee
//
// Account-keyed credential storage — the multi-login replacement for the
// instance-keyed InstanceCredentialsManager.
//
import Foundation
/// Credential storage keyed by `Account`, not by `Instance`.
///
/// Adopted by `InvidiousCredentialsManager` and `PipedCredentialsManager`.
/// `BasicAuthCredentialsManager` deliberately does **not** adopt it and keeps
/// `InstanceCredentialsManager`: HTTP Basic auth is a property of the instance
/// (or the reverse proxy in front of it), not of a user account, and a single
/// proxy credential covers every account on that instance.
@MainActor
protocol AccountCredentialsManager: AnyObject, Observable {
/// Whether the identity scope has been resolved yet.
///
/// While `false`, every read below reports "no credential" because the app
/// cannot yet tell which scope to look in. Callers must render that as
/// *temporarily unavailable*, never as "signed out" — correcting state on
/// it would discard a perfectly good login.
var isScopeResolved: Bool { get }
func setCredential(_ credential: String, for account: Account)
func credential(for account: Account) -> String?
func deleteCredential(for account: Account)
/// Deletes the credential and stored login identifier for an account this
/// device may know nothing else about.
///
/// A deletion can reach this device before the account it deletes does —
/// the tombstone and the account are separate records, and iCloud Keychain
/// runs on its own timeline. The secret must go regardless, and the scoped
/// key needs only the account's ID.
func deleteCredential(forAccountID accountID: UUID)
/// The login identifier the user typed. Kept in the Keychain beside the
/// credential so it never enters synced metadata.
func setUsername(_ username: String, for account: Account)
func username(for account: Account) -> String?
func isLoggedIn(for account: Account) -> Bool
func refreshLoginStatus(for account: Account)
// MARK: - Identity migration
/// Moves an account's credential and username from `sourceScope` into the
/// current one, for a record being imported out of the `local` namespace
/// when an Apple account claims it.
///
/// - Returns: `true` only when `sourceScope` holds nothing for this account
/// any more — credential and username both. `false` means something is
/// still keyed to the source, so the record it belongs to must stay where
/// it is: that record is what brings the retry.
@discardableResult
func migrateCredential(forAccountID accountID: UUID, fromScope sourceScope: String) -> Bool
/// Deletes an account's credential and username under a named scope.
///
/// The scoped counterpart of `deleteCredential(forAccountID:)`: an import
/// refused because this identity already tombstoned the account has to take
/// the secret with it, and that secret is keyed by the scope being left
/// rather than the one in force.
///
/// - Returns: whether that scope is now free of the account, on the same
/// terms as `migrateCredential(forAccountID:fromScope:)`.
@discardableResult
func deleteCredential(forAccountID accountID: UUID, inScope scope: String) -> Bool
// MARK: - Legacy adoption
/// Whether a pre-revamp credential for this account's instance exists on
/// this device and has not been adopted by the current identity.
///
/// True only for the account the instance's legacy login was migrated into
/// — the old item is keyed by instance, and a second login the user added
/// themselves must never inherit it.
func hasAdoptableLegacyCredential(for account: Account) -> Bool
/// Copies the legacy item into this account's scoped key. Returns whether
/// the copy verified.
///
/// Runs by itself as part of recomputing account status: the upgrade is not
/// a decision to put to the user. A retained Keychain item carries no proof
/// of which Apple account it belonged to (measured — see
/// ``CredentialIdentityScope``), but the pre-revamp release read that item
/// with `kSecAttrSynchronizableAny` and no identity in its key, so it was
/// already being served to whoever held the device. The copy is written
/// device-local so this migration cannot put a secret of unproven
/// provenance into the current identity's iCloud Keychain.
@discardableResult
func adoptLegacyCredential(for account: Account) -> Bool
/// Deletes the unscoped legacy item **only** if this identity adopted it
/// and it still holds the adopted secret. Called when an account is really
/// deleted, never on mere absence.
func deleteLegacyCredentialIfOwned(for account: Account)
}