Signed-off-by: Matt Bruce <matt.bruce1@toyota.com>
This commit is contained in:
parent
d16bf6eb2e
commit
f1c579244c
@ -165,11 +165,36 @@ final class GameSettings {
|
||||
|
||||
// MARK: - iCloud
|
||||
|
||||
private let iCloudStore = NSUbiquitousKeyValueStore.default
|
||||
/// Cached reference to iCloud store, lazily initialized
|
||||
private var _iCloudStore: NSUbiquitousKeyValueStore?
|
||||
private var _iCloudStoreInitialized = false
|
||||
|
||||
private var iCloudStore: NSUbiquitousKeyValueStore? {
|
||||
// Return cached value if already attempted initialization
|
||||
if _iCloudStoreInitialized {
|
||||
return _iCloudStore
|
||||
}
|
||||
|
||||
_iCloudStoreInitialized = true
|
||||
|
||||
// Only access the store if iCloud is actually available
|
||||
guard iCloudAvailable else {
|
||||
return nil
|
||||
}
|
||||
|
||||
_iCloudStore = NSUbiquitousKeyValueStore.default
|
||||
return _iCloudStore
|
||||
}
|
||||
|
||||
/// Whether iCloud is available.
|
||||
var iCloudAvailable: Bool {
|
||||
FileManager.default.ubiquityIdentityToken != nil
|
||||
guard FileManager.default.ubiquityIdentityToken != nil else {
|
||||
return false
|
||||
}
|
||||
|
||||
// Additional check: verify we can actually access ubiquity container
|
||||
let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)
|
||||
return containerURL != nil
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
@ -177,20 +202,19 @@ final class GameSettings {
|
||||
init() {
|
||||
load()
|
||||
|
||||
// Register for iCloud changes
|
||||
// Register for iCloud changes (only if available)
|
||||
if iCloudAvailable, let store = iCloudStore {
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
|
||||
object: iCloudStore,
|
||||
object: store,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
Task { @MainActor in
|
||||
// Already on main queue, safe to call
|
||||
self?.loadFromiCloud()
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger iCloud sync
|
||||
if iCloudAvailable {
|
||||
iCloudStore.synchronize()
|
||||
store.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,51 +284,51 @@ final class GameSettings {
|
||||
|
||||
/// Loads settings from iCloud.
|
||||
private func loadFromiCloud() {
|
||||
guard iCloudAvailable else { return }
|
||||
guard iCloudAvailable, let store = iCloudStore else { return }
|
||||
|
||||
if let rawDeckCount = iCloudStore.object(forKey: Keys.deckCount) as? Int,
|
||||
if let rawDeckCount = store.object(forKey: Keys.deckCount) as? Int,
|
||||
let deckCount = DeckCount(rawValue: rawDeckCount) {
|
||||
self.deckCount = deckCount
|
||||
}
|
||||
|
||||
if let rawTableLimits = iCloudStore.string(forKey: Keys.tableLimits),
|
||||
if let rawTableLimits = store.string(forKey: Keys.tableLimits),
|
||||
let tableLimits = TableLimits(rawValue: rawTableLimits) {
|
||||
self.tableLimits = tableLimits
|
||||
}
|
||||
|
||||
if let balance = iCloudStore.object(forKey: Keys.startingBalance) as? Int {
|
||||
if let balance = store.object(forKey: Keys.startingBalance) as? Int {
|
||||
self.startingBalance = balance
|
||||
}
|
||||
|
||||
if iCloudStore.object(forKey: Keys.showAnimations) != nil {
|
||||
self.showAnimations = iCloudStore.bool(forKey: Keys.showAnimations)
|
||||
if store.object(forKey: Keys.showAnimations) != nil {
|
||||
self.showAnimations = store.bool(forKey: Keys.showAnimations)
|
||||
}
|
||||
|
||||
if let speed = iCloudStore.object(forKey: Keys.dealingSpeed) as? Double {
|
||||
if let speed = store.object(forKey: Keys.dealingSpeed) as? Double {
|
||||
self.dealingSpeed = speed
|
||||
}
|
||||
|
||||
if iCloudStore.object(forKey: Keys.showCardsRemaining) != nil {
|
||||
self.showCardsRemaining = iCloudStore.bool(forKey: Keys.showCardsRemaining)
|
||||
if store.object(forKey: Keys.showCardsRemaining) != nil {
|
||||
self.showCardsRemaining = store.bool(forKey: Keys.showCardsRemaining)
|
||||
}
|
||||
|
||||
if iCloudStore.object(forKey: Keys.showHistory) != nil {
|
||||
self.showHistory = iCloudStore.bool(forKey: Keys.showHistory)
|
||||
if store.object(forKey: Keys.showHistory) != nil {
|
||||
self.showHistory = store.bool(forKey: Keys.showHistory)
|
||||
}
|
||||
|
||||
if iCloudStore.object(forKey: Keys.showHints) != nil {
|
||||
self.showHints = iCloudStore.bool(forKey: Keys.showHints)
|
||||
if store.object(forKey: Keys.showHints) != nil {
|
||||
self.showHints = store.bool(forKey: Keys.showHints)
|
||||
}
|
||||
|
||||
if iCloudStore.object(forKey: Keys.soundEnabled) != nil {
|
||||
self.soundEnabled = iCloudStore.bool(forKey: Keys.soundEnabled)
|
||||
if store.object(forKey: Keys.soundEnabled) != nil {
|
||||
self.soundEnabled = store.bool(forKey: Keys.soundEnabled)
|
||||
}
|
||||
|
||||
if iCloudStore.object(forKey: Keys.hapticsEnabled) != nil {
|
||||
self.hapticsEnabled = iCloudStore.bool(forKey: Keys.hapticsEnabled)
|
||||
if store.object(forKey: Keys.hapticsEnabled) != nil {
|
||||
self.hapticsEnabled = store.bool(forKey: Keys.hapticsEnabled)
|
||||
}
|
||||
|
||||
if let volume = iCloudStore.object(forKey: Keys.soundVolume) as? Double {
|
||||
if let volume = store.object(forKey: Keys.soundVolume) as? Double {
|
||||
self.soundVolume = Float(volume)
|
||||
}
|
||||
}
|
||||
@ -326,19 +350,19 @@ final class GameSettings {
|
||||
defaults.set(soundVolume, forKey: Keys.soundVolume)
|
||||
|
||||
// Also save to iCloud
|
||||
if iCloudAvailable {
|
||||
iCloudStore.set(deckCount.rawValue, forKey: Keys.deckCount)
|
||||
iCloudStore.set(tableLimits.rawValue, forKey: Keys.tableLimits)
|
||||
iCloudStore.set(startingBalance, forKey: Keys.startingBalance)
|
||||
iCloudStore.set(showAnimations, forKey: Keys.showAnimations)
|
||||
iCloudStore.set(dealingSpeed, forKey: Keys.dealingSpeed)
|
||||
iCloudStore.set(showCardsRemaining, forKey: Keys.showCardsRemaining)
|
||||
iCloudStore.set(showHistory, forKey: Keys.showHistory)
|
||||
iCloudStore.set(showHints, forKey: Keys.showHints)
|
||||
iCloudStore.set(soundEnabled, forKey: Keys.soundEnabled)
|
||||
iCloudStore.set(hapticsEnabled, forKey: Keys.hapticsEnabled)
|
||||
iCloudStore.set(Double(soundVolume), forKey: Keys.soundVolume)
|
||||
iCloudStore.synchronize()
|
||||
if iCloudAvailable, let store = iCloudStore {
|
||||
store.set(deckCount.rawValue, forKey: Keys.deckCount)
|
||||
store.set(tableLimits.rawValue, forKey: Keys.tableLimits)
|
||||
store.set(startingBalance, forKey: Keys.startingBalance)
|
||||
store.set(showAnimations, forKey: Keys.showAnimations)
|
||||
store.set(dealingSpeed, forKey: Keys.dealingSpeed)
|
||||
store.set(showCardsRemaining, forKey: Keys.showCardsRemaining)
|
||||
store.set(showHistory, forKey: Keys.showHistory)
|
||||
store.set(showHints, forKey: Keys.showHints)
|
||||
store.set(soundEnabled, forKey: Keys.soundEnabled)
|
||||
store.set(hapticsEnabled, forKey: Keys.hapticsEnabled)
|
||||
store.set(Double(soundVolume), forKey: Keys.soundVolume)
|
||||
store.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3318,6 +3318,190 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"History Display" : {
|
||||
"comment" : "Title of a section in the Rules Help view explaining the history feature.",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "History Display"
|
||||
}
|
||||
},
|
||||
"es-MX" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Visualización del historial"
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Affichage de l'historique"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"The History shows all previous round results at a glance." : {
|
||||
"comment" : "Explains the purpose of the history display.",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "The History shows all previous round results at a glance."
|
||||
}
|
||||
},
|
||||
"es-MX" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "El Historial muestra todos los resultados de rondas anteriores de un vistazo."
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "L'historique affiche tous les résultats des tours précédents en un coup d'œil."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Blue Circle (P): Player won the hand" : {
|
||||
"comment" : "Explains the blue circle icon in the history.",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Blue Circle (P): Player won the hand"
|
||||
}
|
||||
},
|
||||
"es-MX" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Círculo Azul (P): El jugador ganó la mano"
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Cercle Bleu (P) : Le joueur a gagné la main"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Red Circle (B): Banker won the hand" : {
|
||||
"comment" : "Explains the red circle icon in the history.",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Red Circle (B): Banker won the hand"
|
||||
}
|
||||
},
|
||||
"es-MX" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Círculo Rojo (B): El banquero ganó la mano"
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Cercle Rouge (B) : Le banquier a gagné la main"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Green Circle (T): Tie between Player and Banker" : {
|
||||
"comment" : "Explains the green circle icon in the history.",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Green Circle (T): Tie between Player and Banker"
|
||||
}
|
||||
},
|
||||
"es-MX" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Círculo Verde (T): Empate entre el jugador y el banquero"
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Cercle Vert (T) : Égalité entre le joueur et le banquier"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Yellow Dot (bottom-left): A pair occurred in that hand" : {
|
||||
"comment" : "Explains the yellow dot marker in the history.",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Yellow Dot (bottom-left): A pair occurred in that hand"
|
||||
}
|
||||
},
|
||||
"es-MX" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Punto Amarillo (abajo-izquierda): Hubo un par en esa mano"
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Point Jaune (en bas à gauche) : Une paire s'est produite dans cette main"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Yellow Star (top-right): Natural 8 or 9 win" : {
|
||||
"comment" : "Explains the yellow star marker in the history.",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Yellow Star (top-right): Natural 8 or 9 win"
|
||||
}
|
||||
},
|
||||
"es-MX" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Estrella Amarilla (arriba-derecha): Victoria natural con 8 o 9"
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Étoile Jaune (en haut à droite) : Victoire naturelle avec 8 ou 9"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Use History to spot patterns and trends in the shoe." : {
|
||||
"comment" : "Advises the player on how to use the history feature.",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Use History to spot patterns and trends in the shoe."
|
||||
}
|
||||
},
|
||||
"es-MX" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Usa el Historial para detectar patrones y tendencias en el zapato."
|
||||
}
|
||||
},
|
||||
"fr-CA" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Utilisez l'historique pour repérer les modèles et les tendances dans le sabot."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Strategy Tips" : {
|
||||
"comment" : "Title of a section in the Rules Help view focused on strategy tips.",
|
||||
"localizations" : {
|
||||
|
||||
@ -14,7 +14,7 @@ import CasinoKit
|
||||
enum Design {
|
||||
|
||||
/// Set to true to show layout debug borders on views
|
||||
static let showDebugBorders = true
|
||||
static let showDebugBorders = false
|
||||
|
||||
// MARK: - Shared Constants (from CasinoKit)
|
||||
|
||||
@ -59,10 +59,10 @@ enum Design {
|
||||
// MARK: - Card Deal Animation
|
||||
|
||||
enum DealAnimation {
|
||||
/// Horizontal offset for card deal (from upper-center, simulating dealer)
|
||||
static let offsetX: CGFloat = 0
|
||||
/// Vertical offset for card deal (from above the table)
|
||||
static let offsetY: CGFloat = -250
|
||||
/// Horizontal offset for card deal (shoe position in upper-right area)
|
||||
static let offsetX: CGFloat = 150
|
||||
/// Vertical offset for card deal (from top of screen where shoe is positioned)
|
||||
static let offsetY: CGFloat = -300
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -103,6 +103,19 @@ struct RulesHelpView: View {
|
||||
String(localized: "Independent of the main game result.")
|
||||
]
|
||||
),
|
||||
RulePage(
|
||||
title: String(localized: "History Display"),
|
||||
icon: "clock.fill",
|
||||
content: [
|
||||
String(localized: "The History shows all previous round results at a glance."),
|
||||
String(localized: "Blue Circle (P): Player won the hand"),
|
||||
String(localized: "Red Circle (B): Banker won the hand"),
|
||||
String(localized: "Green Circle (T): Tie between Player and Banker"),
|
||||
String(localized: "Yellow Dot (bottom-left): A pair occurred in that hand"),
|
||||
String(localized: "Yellow Star (top-right): Natural 8 or 9 win"),
|
||||
String(localized: "Use History to spot patterns and trends in the shoe.")
|
||||
]
|
||||
),
|
||||
RulePage(
|
||||
title: String(localized: "Strategy Tips"),
|
||||
icon: "lightbulb.fill",
|
||||
@ -195,7 +208,7 @@ struct RulePageView: View {
|
||||
// Content
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||
ForEach(page.content.indices, id: \.self) { index in
|
||||
HStack(alignment: .top, spacing: Design.Spacing.medium) {
|
||||
HStack(alignment: .firstTextBaseline, spacing: Design.Spacing.medium) {
|
||||
Text("•")
|
||||
.foregroundStyle(Color.Sheet.accent)
|
||||
|
||||
|
||||
@ -303,7 +303,7 @@ struct RulePageView: View {
|
||||
// Content
|
||||
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
|
||||
ForEach(page.content.indices, id: \.self) { index in
|
||||
HStack(alignment: .top, spacing: Design.Spacing.medium) {
|
||||
HStack(alignment: .firstTextBaseline, spacing: Design.Spacing.medium) {
|
||||
Text("•")
|
||||
.foregroundStyle(Color.Sheet.accent)
|
||||
|
||||
|
||||
@ -37,6 +37,16 @@ public final class CloudSyncManager<T: PersistableGameData> {
|
||||
public var iCloudAvailable: Bool {
|
||||
let token = FileManager.default.ubiquityIdentityToken
|
||||
let available = token != nil
|
||||
|
||||
// Additional check: verify we can actually access ubiquity container
|
||||
// This prevents false positives in simulators
|
||||
if available {
|
||||
let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)
|
||||
let actuallyAvailable = containerURL != nil
|
||||
CasinoDesign.debugLog("CloudSyncManager: iCloud token = \(String(describing: token)), container = \(String(describing: containerURL))")
|
||||
return actuallyAvailable
|
||||
}
|
||||
|
||||
CasinoDesign.debugLog("CloudSyncManager: iCloud available = \(available), token = \(String(describing: token))")
|
||||
return available
|
||||
}
|
||||
@ -61,7 +71,29 @@ public final class CloudSyncManager<T: PersistableGameData> {
|
||||
|
||||
// MARK: - Private Properties
|
||||
|
||||
private let iCloudStore = NSUbiquitousKeyValueStore.default
|
||||
/// Cached reference to iCloud store, lazily initialized
|
||||
private var _iCloudStore: NSUbiquitousKeyValueStore?
|
||||
private var _iCloudStoreInitialized = false
|
||||
|
||||
private var iCloudStore: NSUbiquitousKeyValueStore? {
|
||||
// Return cached value if already attempted initialization
|
||||
if _iCloudStoreInitialized {
|
||||
return _iCloudStore
|
||||
}
|
||||
|
||||
_iCloudStoreInitialized = true
|
||||
|
||||
// Only access the store if iCloud is actually available
|
||||
guard iCloudAvailable else {
|
||||
CasinoDesign.debugLog("CloudSyncManager[\(T.gameIdentifier)]: iCloud not available, skipping NSUbiquitousKeyValueStore access")
|
||||
return nil
|
||||
}
|
||||
|
||||
CasinoDesign.debugLog("CloudSyncManager[\(T.gameIdentifier)]: Accessing NSUbiquitousKeyValueStore.default...")
|
||||
_iCloudStore = NSUbiquitousKeyValueStore.default
|
||||
CasinoDesign.debugLog("CloudSyncManager[\(T.gameIdentifier)]: NSUbiquitousKeyValueStore.default accessed successfully")
|
||||
return _iCloudStore
|
||||
}
|
||||
private let encoder = JSONEncoder()
|
||||
private let decoder = JSONDecoder()
|
||||
|
||||
@ -87,10 +119,11 @@ public final class CloudSyncManager<T: PersistableGameData> {
|
||||
UserDefaults.standard.set(true, forKey: iCloudEnabledKey)
|
||||
}
|
||||
|
||||
// Register for iCloud changes BEFORE syncing
|
||||
// Register for iCloud changes BEFORE syncing (only if available)
|
||||
if iCloudAvailable, let store = iCloudStore {
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
|
||||
object: iCloudStore,
|
||||
object: store,
|
||||
queue: .main
|
||||
) { [weak self] notification in
|
||||
// Extract values before crossing isolation boundary (for Sendable compliance)
|
||||
@ -105,8 +138,9 @@ public final class CloudSyncManager<T: PersistableGameData> {
|
||||
}
|
||||
|
||||
// Trigger iCloud sync FIRST (before loading local)
|
||||
if iCloudAvailable && iCloudEnabled {
|
||||
iCloudStore.synchronize()
|
||||
if iCloudEnabled {
|
||||
store.synchronize()
|
||||
}
|
||||
}
|
||||
|
||||
// Load data (may get updated when iCloud sync completes)
|
||||
@ -129,14 +163,20 @@ public final class CloudSyncManager<T: PersistableGameData> {
|
||||
CasinoDesign.debugLog("CloudSyncManager[\(T.gameIdentifier)]: Delayed check - forcing sync...")
|
||||
|
||||
// Force another sync (on main thread to avoid concurrency warning)
|
||||
guard let store = iCloudStore else {
|
||||
CasinoDesign.debugLog("CloudSyncManager[\(T.gameIdentifier)]: iCloud store unavailable")
|
||||
hasCompletedInitialSync = true
|
||||
return
|
||||
}
|
||||
|
||||
var syncResult = false
|
||||
await MainActor.run {
|
||||
syncResult = iCloudStore.synchronize()
|
||||
syncResult = store.synchronize()
|
||||
}
|
||||
CasinoDesign.debugLog("CloudSyncManager[\(T.gameIdentifier)]: synchronize() returned \(syncResult)")
|
||||
|
||||
// Check what's in the store
|
||||
let allKeys = iCloudStore.dictionaryRepresentation.keys
|
||||
let allKeys = store.dictionaryRepresentation.keys
|
||||
CasinoDesign.debugLog("CloudSyncManager[\(T.gameIdentifier)]: iCloud store keys: \(Array(allKeys))")
|
||||
|
||||
// Try loading cloud data again
|
||||
@ -186,10 +226,10 @@ public final class CloudSyncManager<T: PersistableGameData> {
|
||||
UserDefaults.standard.set(encoded, forKey: localKey)
|
||||
|
||||
// Save to iCloud
|
||||
if iCloudAvailable && iCloudEnabled {
|
||||
iCloudStore.set(encoded, forKey: cloudKey)
|
||||
iCloudStore.set(Date(), forKey: syncDateKey)
|
||||
iCloudStore.synchronize()
|
||||
if iCloudAvailable && iCloudEnabled, let store = iCloudStore {
|
||||
store.set(encoded, forKey: cloudKey)
|
||||
store.set(Date(), forKey: syncDateKey)
|
||||
store.synchronize()
|
||||
lastSyncDate = Date()
|
||||
syncStatus = "Synced"
|
||||
}
|
||||
@ -258,12 +298,13 @@ public final class CloudSyncManager<T: PersistableGameData> {
|
||||
|
||||
private func loadCloud() -> T? {
|
||||
guard iCloudAvailable && iCloudEnabled,
|
||||
let data = iCloudStore.data(forKey: cloudKey),
|
||||
let store = iCloudStore,
|
||||
let data = store.data(forKey: cloudKey),
|
||||
let decoded = try? decoder.decode(T.self, from: data) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if let syncDate = iCloudStore.object(forKey: syncDateKey) as? Date {
|
||||
if let syncDate = store.object(forKey: syncDateKey) as? Date {
|
||||
lastSyncDate = syncDate
|
||||
}
|
||||
|
||||
@ -279,10 +320,15 @@ public final class CloudSyncManager<T: PersistableGameData> {
|
||||
return
|
||||
}
|
||||
|
||||
guard let store = iCloudStore else {
|
||||
syncStatus = "iCloud unavailable"
|
||||
return
|
||||
}
|
||||
|
||||
isSyncing = true
|
||||
syncStatus = "Syncing..."
|
||||
|
||||
iCloudStore.synchronize()
|
||||
store.synchronize()
|
||||
|
||||
// Reload to get any changes
|
||||
let latestData = load()
|
||||
@ -347,10 +393,10 @@ public final class CloudSyncManager<T: PersistableGameData> {
|
||||
public func reset() {
|
||||
UserDefaults.standard.removeObject(forKey: localKey)
|
||||
|
||||
if iCloudAvailable {
|
||||
iCloudStore.removeObject(forKey: cloudKey)
|
||||
iCloudStore.removeObject(forKey: syncDateKey)
|
||||
iCloudStore.synchronize()
|
||||
if iCloudAvailable, let store = iCloudStore {
|
||||
store.removeObject(forKey: cloudKey)
|
||||
store.removeObject(forKey: syncDateKey)
|
||||
store.synchronize()
|
||||
}
|
||||
|
||||
data = T.empty
|
||||
|
||||
@ -13,13 +13,13 @@ public enum CasinoDesign {
|
||||
// MARK: - Debug
|
||||
|
||||
/// Set to true to enable debug logging in CasinoKit.
|
||||
public static let showDebugLogs = false
|
||||
public static let showDebugLogs = true
|
||||
|
||||
/// Logs a message only in debug builds when `showDebugLogs` is enabled.
|
||||
public static func debugLog(_ message: String) {
|
||||
#if DEBUG
|
||||
if showDebugLogs {
|
||||
print(message)
|
||||
print("[CasinoKit] \(message)")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user