Skip to content

Commit ce82ab8

Browse files
committed
WIP 4
1 parent 105fcd7 commit ce82ab8

File tree

1 file changed

+73
-135
lines changed

1 file changed

+73
-135
lines changed

LoopFollow/Settings/ImportExport/ImportExportSettingsViewModel.swift

Lines changed: 73 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -238,33 +238,15 @@ class ImportExportSettingsViewModel: ObservableObject {
238238
exportedAlarmIds.removeAll()
239239
}
240240

241-
// MARK: - iCloud Methods (using CloudKit)
241+
// MARK: - iCloud Methods (using Key-Value Storage)
242242

243-
private let cloudKitRecordType = "AppSettings"
244-
private var cloudKitRecordID: CKRecord.ID {
245-
CKRecord.ID(recordName: "\(AppConstants.appInstanceId)_settings")
243+
private var iCloudSettingsKey: String {
244+
"\(AppConstants.appInstanceId)_settings"
246245
}
247246

248-
/// Check if CloudKit is available
249-
private func checkCloudKitAvailability(completion: @escaping (Bool, String?) -> Void) {
250-
CKContainer.default().accountStatus { status, error in
251-
DispatchQueue.main.async {
252-
switch status {
253-
case .available:
254-
completion(true, nil)
255-
case .noAccount:
256-
completion(false, "iCloud is not available. Please sign in to iCloud in Settings.")
257-
case .restricted:
258-
completion(false, "iCloud access is restricted on this device.")
259-
case .couldNotDetermine:
260-
completion(false, "Could not determine iCloud status: \(error?.localizedDescription ?? "Unknown error")")
261-
case .temporarilyUnavailable:
262-
completion(false, "iCloud is temporarily unavailable. Please try again later.")
263-
@unknown default:
264-
completion(false, "Unknown iCloud status.")
265-
}
266-
}
267-
}
247+
/// Check if iCloud Key-Value Storage is available
248+
private func isICloudAvailable() -> Bool {
249+
return FileManager.default.ubiquityIdentityToken != nil
268250
}
269251

270252
func exportToiCloud() {
@@ -287,130 +269,86 @@ class ImportExportSettingsViewModel: ObservableObject {
287269
return
288270
}
289271

290-
LogManager.shared.log(category: .general, message: "Attempting to export settings to CloudKit")
272+
guard isICloudAvailable() else {
273+
qrCodeErrorMessage = "iCloud is not available. Please sign in to iCloud in Settings."
274+
LogManager.shared.log(category: .general, message: "iCloud not available for export")
275+
return
276+
}
291277

292-
checkCloudKitAvailability { [weak self] available, errorMessage in
293-
guard let self = self else { return }
278+
LogManager.shared.log(category: .general, message: "Attempting to export settings to iCloud Key-Value Storage")
294279

295-
if !available {
296-
self.qrCodeErrorMessage = errorMessage ?? "iCloud is not available."
297-
LogManager.shared.log(category: .general, message: "CloudKit not available: \(errorMessage ?? "Unknown")")
298-
return
299-
}
280+
let store = NSUbiquitousKeyValueStore.default
281+
store.set(jsonString, forKey: iCloudSettingsKey)
282+
store.set(Date(), forKey: "\(iCloudSettingsKey)_date")
283+
store.set(AppVersionManager().version(), forKey: "\(iCloudSettingsKey)_version")
300284

301-
let privateDatabase = CKContainer.default().privateCloudDatabase
285+
// Explicitly synchronize to push changes
286+
let synchronized = store.synchronize()
287+
LogManager.shared.log(category: .general, message: "iCloud KVS synchronize called, result: \(synchronized)")
302288

303-
// First try to fetch existing record to update it, or create new one
304-
privateDatabase.fetch(withRecordID: self.cloudKitRecordID) { [weak self] existingRecord, error in
305-
guard let self = self else { return }
289+
// Build export details for the success alert
290+
var details: [String] = []
306291

307-
let record: CKRecord
308-
if let existing = existingRecord {
309-
record = existing
310-
} else {
311-
record = CKRecord(recordType: self.cloudKitRecordType, recordID: self.cloudKitRecordID)
312-
}
313-
314-
record["settingsJSON"] = jsonString as CKRecordValue
315-
record["exportDate"] = Date() as CKRecordValue
316-
record["appVersion"] = AppVersionManager().version() as CKRecordValue
317-
318-
privateDatabase.save(record) { [weak self] _, saveError in
319-
DispatchQueue.main.async {
320-
guard let self = self else { return }
321-
322-
if let saveError = saveError {
323-
self.qrCodeErrorMessage = "Failed to export to iCloud: \(saveError.localizedDescription)"
324-
LogManager.shared.log(category: .general, message: "CloudKit save failed: \(saveError.localizedDescription)")
325-
return
326-
}
327-
328-
// Build export details for the success alert
329-
var details: [String] = []
330-
331-
if !nightscoutSettings.url.isEmpty {
332-
details.append("Nightscout: \(nightscoutSettings.url)")
333-
}
334-
if !dexcomSettings.userName.isEmpty {
335-
details.append("Dexcom: \(dexcomSettings.userName)")
336-
}
337-
if remoteSettings.remoteType != .none {
338-
details.append("Remote: \(remoteSettings.remoteType.rawValue)")
339-
}
340-
if !alarmSettings.alarms.isEmpty {
341-
details.append("Alarms: \(alarmSettings.alarms.count) alarm(s)")
342-
}
343-
344-
self.exportSuccessDetails = details
345-
self.exportSuccessMessage = "Settings saved to iCloud"
346-
self.showExportSuccessAlert = true
347-
self.qrCodeErrorMessage = ""
348-
349-
LogManager.shared.log(category: .general, message: "All settings exported to CloudKit successfully")
350-
}
351-
}
352-
}
292+
if !nightscoutSettings.url.isEmpty {
293+
details.append("Nightscout: \(nightscoutSettings.url)")
294+
}
295+
if !dexcomSettings.userName.isEmpty {
296+
details.append("Dexcom: \(dexcomSettings.userName)")
353297
}
298+
if remoteSettings.remoteType != .none {
299+
details.append("Remote: \(remoteSettings.remoteType.rawValue)")
300+
}
301+
if !alarmSettings.alarms.isEmpty {
302+
details.append("Alarms: \(alarmSettings.alarms.count) alarm(s)")
303+
}
304+
305+
exportSuccessDetails = details
306+
exportSuccessMessage = "Settings saved to iCloud"
307+
showExportSuccessAlert = true
308+
qrCodeErrorMessage = ""
309+
310+
LogManager.shared.log(category: .general, message: "All settings exported to iCloud Key-Value Storage successfully")
354311
}
355312

356313
func importFromiCloud() {
357-
LogManager.shared.log(category: .general, message: "Attempting to import settings from CloudKit")
314+
LogManager.shared.log(category: .general, message: "Attempting to import settings from iCloud Key-Value Storage")
358315

359-
checkCloudKitAvailability { [weak self] available, errorMessage in
360-
guard let self = self else { return }
316+
guard isICloudAvailable() else {
317+
importNotFoundMessage = "iCloud is not available.\n\nPlease sign in to iCloud in Settings."
318+
showImportNotFoundAlert = true
319+
LogManager.shared.log(category: .general, message: "iCloud not available for import")
320+
return
321+
}
361322

362-
if !available {
363-
self.importNotFoundMessage = errorMessage ?? "iCloud is not available."
364-
self.showImportNotFoundAlert = true
365-
LogManager.shared.log(category: .general, message: "CloudKit not available for import: \(errorMessage ?? "Unknown")")
366-
return
367-
}
323+
let store = NSUbiquitousKeyValueStore.default
368324

369-
let privateDatabase = CKContainer.default().privateCloudDatabase
370-
371-
privateDatabase.fetch(withRecordID: self.cloudKitRecordID) { [weak self] record, error in
372-
DispatchQueue.main.async {
373-
guard let self = self else { return }
374-
375-
if let error = error {
376-
if let ckError = error as? CKError, ckError.code == .unknownItem {
377-
self.importNotFoundMessage = "No settings file found in iCloud.\n\nMake sure you have previously exported settings to iCloud from this app."
378-
self.showImportNotFoundAlert = true
379-
LogManager.shared.log(category: .general, message: "No CloudKit record found")
380-
} else {
381-
self.qrCodeErrorMessage = "Failed to import from iCloud: \(error.localizedDescription)"
382-
LogManager.shared.log(category: .general, message: "CloudKit fetch failed: \(error.localizedDescription)")
383-
}
384-
return
385-
}
386-
387-
guard let record = record,
388-
let jsonString = record["settingsJSON"] as? String,
389-
let jsonData = jsonString.data(using: .utf8) else {
390-
self.importNotFoundMessage = "No settings file found in iCloud.\n\nMake sure you have previously exported settings to iCloud from this app."
391-
self.showImportNotFoundAlert = true
392-
return
393-
}
394-
395-
LogManager.shared.log(category: .general, message: "Settings record found in CloudKit, attempting to decode")
396-
397-
guard let settings = SettingsMigrationManager.migrateSettings(jsonData) else {
398-
self.qrCodeErrorMessage = "Failed to decode settings from iCloud"
399-
return
400-
}
401-
402-
// Check version compatibility
403-
if !SettingsMigrationManager.isCompatibleVersion(settings.appVersion) {
404-
self.qrCodeErrorMessage = SettingsMigrationManager.getCompatibilityMessage(for: settings.appVersion)
405-
}
406-
407-
// Store settings and create preview for confirmation
408-
self.pendingImportSettings = settings
409-
self.pendingImportSource = "iCloud"
410-
self.createImportPreview(from: settings)
411-
}
412-
}
325+
// Synchronize to get latest from iCloud
326+
store.synchronize()
327+
328+
guard let jsonString = store.string(forKey: iCloudSettingsKey),
329+
let jsonData = jsonString.data(using: .utf8) else {
330+
importNotFoundMessage = "No settings file found in iCloud.\n\nMake sure you have previously exported settings to iCloud from this app."
331+
showImportNotFoundAlert = true
332+
LogManager.shared.log(category: .general, message: "No settings found in iCloud Key-Value Storage")
333+
return
334+
}
335+
336+
LogManager.shared.log(category: .general, message: "Settings found in iCloud Key-Value Storage, attempting to decode")
337+
338+
guard let settings = SettingsMigrationManager.migrateSettings(jsonData) else {
339+
qrCodeErrorMessage = "Failed to decode settings from iCloud"
340+
return
341+
}
342+
343+
// Check version compatibility
344+
if !SettingsMigrationManager.isCompatibleVersion(settings.appVersion) {
345+
qrCodeErrorMessage = SettingsMigrationManager.getCompatibilityMessage(for: settings.appVersion)
413346
}
347+
348+
// Store settings and create preview for confirmation
349+
pendingImportSettings = settings
350+
pendingImportSource = "iCloud"
351+
createImportPreview(from: settings)
414352
}
415353

416354
private func createImportPreview(from settings: CombinedSettingsExport) {

0 commit comments

Comments
 (0)