feat: v2
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user