50 lines
2.1 KiB
Swift
50 lines
2.1 KiB
Swift
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)
|
||
}
|
||
}
|