This commit is contained in:
2026-08-12 07:06:01 +07:00
parent 1025658518
commit 9bc96dbc2d
39 changed files with 1682 additions and 658 deletions
+49
View File
@@ -0,0 +1,49 @@
import Foundation
// MARK: - App Group
/// Единая точка правды об App Group: идентификатор и пути к общему хранилищу.
/// Идентификатор обязан совпадать со значением в entitlements обоих таргетов.
///
/// `nonisolated` инфраструктура не привязана к UI: в app-таргете включена
/// изоляция MainActor по умолчанию, а сюда ходят и акторы, и провайдер виджета.
nonisolated enum AppGroup {
static let id = "group.robonen.rapira-market-widget.crypto"
/// UserDefaults, видимые и приложению, и виджету.
static var defaults: UserDefaults? {
UserDefaults(suiteName: id)
}
/// Каталог дисковых кэшей в контейнере группы.
/// nil если entitlements не настроены; кэширование тогда просто отключается.
static var cachesURL: URL? {
FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: id)?
.appendingPathComponent("Library/Caches", isDirectory: true)
}
}
// MARK: - Выбор пар
/// Пары, выбранные пользователем для виджета. Порядок сохраняется:
/// первая пара «герой» маленького и большого виджетов.
nonisolated struct PairSelectionStore: Sendable {
static let shared = PairSelectionStore()
/// Стартовый набор до первой настройки.
static let defaultPairs = ["BTC/USDT", "ETH/USDT", "SOL/USDT"]
/// Максимум пар: столько вмещает большой виджет (1 крупная + 4 строки).
static let maxSelection = 5
private let key = "selectedPairs"
func load() -> [String] {
AppGroup.defaults?.stringArray(forKey: key) ?? Self.defaultPairs
}
func save(_ pairs: [String]) {
AppGroup.defaults?.set(pairs, forKey: key)
}
}