diff --git a/ios/OpenClimb.xcodeproj/project.pbxproj b/ios/OpenClimb.xcodeproj/project.pbxproj index 63bf078..4698134 100644 --- a/ios/OpenClimb.xcodeproj/project.pbxproj +++ b/ios/OpenClimb.xcodeproj/project.pbxproj @@ -396,7 +396,7 @@ CODE_SIGN_ENTITLEMENTS = OpenClimb/OpenClimb.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 11; + CURRENT_PROJECT_VERSION = 12; DEVELOPMENT_TEAM = 4BC9Y2LL4B; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -416,7 +416,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2.0; + MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.OpenClimb; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -439,7 +439,7 @@ CODE_SIGN_ENTITLEMENTS = OpenClimb/OpenClimb.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 11; + CURRENT_PROJECT_VERSION = 12; DEVELOPMENT_TEAM = 4BC9Y2LL4B; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -459,7 +459,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2.0; + MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.OpenClimb; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -481,7 +481,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 11; + CURRENT_PROJECT_VERSION = 12; DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = SessionStatusLive/Info.plist; @@ -492,7 +492,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.2.0; + MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.OpenClimb.SessionStatusLive; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -511,7 +511,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = SessionStatusLiveExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 11; + CURRENT_PROJECT_VERSION = 12; DEVELOPMENT_TEAM = 4BC9Y2LL4B; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = SessionStatusLive/Info.plist; @@ -522,7 +522,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.2.0; + MARKETING_VERSION = 1.2.1; PRODUCT_BUNDLE_IDENTIFIER = com.atridad.OpenClimb.SessionStatusLive; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate b/ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate index 7fb5b30..26db1e9 100644 Binary files a/ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/OpenClimb.xcodeproj/project.xcworkspace/xcuserdata/atridad.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ios/OpenClimb/Services/SyncService.swift b/ios/OpenClimb/Services/SyncService.swift index a88ccc4..d9648c7 100644 --- a/ios/OpenClimb/Services/SyncService.swift +++ b/ios/OpenClimb/Services/SyncService.swift @@ -1,6 +1,5 @@ import Combine import Foundation -import UIKit @MainActor class SyncService: ObservableObject { @@ -455,7 +454,7 @@ class SyncService: ObservableObject { let zipData = try createMinimalZipFromBackup(updatedBackup) // Use existing import method which properly handles data restoration - try dataManager.importData(from: zipData) + try dataManager.importData(from: zipData, showSuccessMessage: false) // Update local data state to match imported data timestamp DataStateManager.shared.setLastModified(backup.exportedAt) @@ -735,180 +734,29 @@ class SyncService: ObservableObject { } func triggerAutoSync(dataManager: ClimbingDataManager) { - guard isConnected && isConfigured && isAutoSyncEnabled else { return } + // Early exit if sync cannot proceed - don't set isSyncing + guard isConnected && isConfigured && isAutoSyncEnabled else { + // Ensure isSyncing is false when sync is not possible + if isSyncing { + isSyncing = false + } + return + } + + // Prevent multiple simultaneous syncs + guard !isSyncing else { + return + } Task { do { try await syncWithServer(dataManager: dataManager) } catch { - print("Auto-sync failed: \(error)") - // Don't show UI errors for auto-sync failures - } - } - } - - // DEPRECATED: Complex merge logic replaced with simple timestamp-based sync - // These methods are no longer used but kept for reference - @available(*, deprecated, message: "Use simple timestamp-based sync instead") - private func performIntelligentMerge(local: ClimbDataBackup, server: ClimbDataBackup) throws - -> ClimbDataBackup - { - print("Merging data - preserving all entities to prevent data loss") - - // Merge gyms by ID, keeping most recently updated - let mergedGyms = mergeGyms(local: local.gyms, server: server.gyms) - - // Merge problems by ID, keeping most recently updated - let mergedProblems = mergeProblems(local: local.problems, server: server.problems) - - // Merge sessions by ID, keeping most recently updated - let mergedSessions = mergeSessions(local: local.sessions, server: server.sessions) - - // Merge attempts by ID, keeping most recently updated - let mergedAttempts = mergeAttempts(local: local.attempts, server: server.attempts) - - print( - "Merge results: gyms=\(mergedGyms.count), problems=\(mergedProblems.count), sessions=\(mergedSessions.count), attempts=\(mergedAttempts.count)" - ) - - return ClimbDataBackup( - exportedAt: ISO8601DateFormatter().string(from: Date()), - version: "2.0", - formatVersion: "2.0", - gyms: mergedGyms, - problems: mergedProblems, - sessions: mergedSessions, - attempts: mergedAttempts - ) - } - - private func mergeGyms(local: [BackupGym], server: [BackupGym]) -> [BackupGym] { - var merged: [String: BackupGym] = [:] - - // Add all local gyms - for gym in local { - merged[gym.id] = gym - } - - // Add server gyms, replacing if newer - for serverGym in server { - if let localGym = merged[serverGym.id] { - // Keep the most recently updated - if isNewerThan(serverGym.updatedAt, localGym.updatedAt) { - merged[serverGym.id] = serverGym + await MainActor.run { + self.isSyncing = false } - } else { - // New gym from server - merged[serverGym.id] = serverGym } } - - return Array(merged.values) - } - - private func mergeProblems(local: [BackupProblem], server: [BackupProblem]) -> [BackupProblem] { - var merged: [String: BackupProblem] = [:] - - // Add all local problems - for problem in local { - merged[problem.id] = problem - } - - // Add server problems, replacing if newer or merging image paths - for serverProblem in server { - if let localProblem = merged[serverProblem.id] { - // Merge image paths from both sources - let localImages = Set(localProblem.imagePaths ?? []) - let serverImages = Set(serverProblem.imagePaths ?? []) - let mergedImages = Array(localImages.union(serverImages)) - - // Use most recently updated problem data but with merged images - let newerProblem = - isNewerThan(serverProblem.updatedAt, localProblem.updatedAt) - ? serverProblem : localProblem - merged[serverProblem.id] = BackupProblem( - id: newerProblem.id, - gymId: newerProblem.gymId, - name: newerProblem.name, - description: newerProblem.description, - climbType: newerProblem.climbType, - difficulty: newerProblem.difficulty, - tags: newerProblem.tags, - location: newerProblem.location, - imagePaths: mergedImages.isEmpty ? nil : mergedImages, - isActive: newerProblem.isActive, - dateSet: newerProblem.dateSet, - notes: newerProblem.notes, - createdAt: newerProblem.createdAt, - updatedAt: newerProblem.updatedAt - ) - } else { - // New problem from server - merged[serverProblem.id] = serverProblem - } - } - - return Array(merged.values) - } - - private func mergeSessions(local: [BackupClimbSession], server: [BackupClimbSession]) - -> [BackupClimbSession] - { - var merged: [String: BackupClimbSession] = [:] - - // Add all local sessions - for session in local { - merged[session.id] = session - } - - // Add server sessions, replacing if newer - for serverSession in server { - if let localSession = merged[serverSession.id] { - // Keep the most recently updated - if isNewerThan(serverSession.updatedAt, localSession.updatedAt) { - merged[serverSession.id] = serverSession - } - } else { - // New session from server - merged[serverSession.id] = serverSession - } - } - - return Array(merged.values) - } - - private func mergeAttempts(local: [BackupAttempt], server: [BackupAttempt]) -> [BackupAttempt] { - var merged: [String: BackupAttempt] = [:] - - // Add all local attempts - for attempt in local { - merged[attempt.id] = attempt - } - - // Add server attempts, replacing if newer - for serverAttempt in server { - if let localAttempt = merged[serverAttempt.id] { - // Keep the most recently created (attempts don't typically get updated) - if isNewerThan(serverAttempt.createdAt, localAttempt.createdAt) { - merged[serverAttempt.id] = serverAttempt - } - } else { - // New attempt from server - merged[serverAttempt.id] = serverAttempt - } - } - - return Array(merged.values) - } - - private func isNewerThan(_ dateString1: String, _ dateString2: String) -> Bool { - let formatter = ISO8601DateFormatter() - guard let date1 = formatter.date(from: dateString1), - let date2 = formatter.date(from: dateString2) - else { - return false - } - return date1 > date2 } func disconnect() { @@ -931,8 +779,6 @@ class SyncService: ObservableObject { } } -// Removed SyncTrigger enum - now using simple auto sync on any data change - enum SyncError: LocalizedError { case notConfigured case notConnected diff --git a/ios/OpenClimb/ViewModels/ClimbingDataManager.swift b/ios/OpenClimb/ViewModels/ClimbingDataManager.swift index 3cad61f..b90e595 100644 --- a/ios/OpenClimb/ViewModels/ClimbingDataManager.swift +++ b/ios/OpenClimb/ViewModels/ClimbingDataManager.swift @@ -32,6 +32,9 @@ class ClimbingDataManager: ObservableObject { // Sync service for automatic syncing let syncService = SyncService() + // Published property to propagate sync state changes + @Published var isSyncing = false + private enum Keys { static let gyms = "openclimb_gyms" static let problems = "openclimb_problems" @@ -67,6 +70,10 @@ class ClimbingDataManager: ObservableObject { migrateImagePaths() setupLiveActivityNotifications() + // Keep our published isSyncing in sync with syncService.isSyncing + syncService.$isSyncing + .assign(to: &$isSyncing) + Task { try? await Task.sleep(nanoseconds: 2_000_000_000) await performImageMaintenance() @@ -206,6 +213,9 @@ class ClimbingDataManager: ObservableObject { DataStateManager.shared.updateDataState() successMessage = "Gym added successfully" clearMessageAfterDelay() + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } func updateGym(_ gym: Gym) { @@ -215,6 +225,9 @@ class ClimbingDataManager: ObservableObject { DataStateManager.shared.updateDataState() successMessage = "Gym updated successfully" clearMessageAfterDelay() + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } } @@ -237,6 +250,9 @@ class ClimbingDataManager: ObservableObject { DataStateManager.shared.updateDataState() successMessage = "Gym deleted successfully" clearMessageAfterDelay() + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } func gym(withId id: UUID) -> Gym? { @@ -261,6 +277,9 @@ class ClimbingDataManager: ObservableObject { DataStateManager.shared.updateDataState() successMessage = "Problem updated successfully" clearMessageAfterDelay() + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } } @@ -276,6 +295,9 @@ class ClimbingDataManager: ObservableObject { problems.removeAll { $0.id == problem.id } saveProblems() DataStateManager.shared.updateDataState() + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } func problem(withId id: UUID) -> Problem? { @@ -291,7 +313,7 @@ class ClimbingDataManager: ObservableObject { } func startSession(gymId: UUID, notes: String? = nil) { - + // End any currently active session if let currentActive = activeSession { endSession(currentActive.id) } @@ -314,6 +336,9 @@ class ClimbingDataManager: ObservableObject { for: newSession, gymName: gym.name) } } + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } func endSession(_ sessionId: UUID) { @@ -358,8 +383,11 @@ class ClimbingDataManager: ObservableObject { successMessage = "Session updated successfully" clearMessageAfterDelay() - // Update Live Activity when session updates + // Update Live Activity when session is updated updateLiveActivityForActiveSession() + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } } @@ -368,7 +396,7 @@ class ClimbingDataManager: ObservableObject { attempts.removeAll { $0.sessionId == session.id } saveAttempts() - // Remove from active session if it's the current one + // If this is the active session, clear it if activeSession?.id == session.id { activeSession = nil saveActiveSession() @@ -380,6 +408,12 @@ class ClimbingDataManager: ObservableObject { DataStateManager.shared.updateDataState() successMessage = "Session deleted successfully" clearMessageAfterDelay() + + // Update Live Activity when session is deleted + updateLiveActivityForActiveSession() + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } func session(withId id: UUID) -> ClimbSession? { @@ -421,6 +455,9 @@ class ClimbingDataManager: ObservableObject { // Update Live Activity when attempt is updated updateLiveActivityForActiveSession() + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } } @@ -433,6 +470,9 @@ class ClimbingDataManager: ObservableObject { // Update Live Activity when attempt is deleted updateLiveActivityForActiveSession() + + // Trigger auto-sync if enabled + syncService.triggerAutoSync(dataManager: self) } func attempts(forSession sessionId: UUID) -> [Attempt] { @@ -476,7 +516,7 @@ class ClimbingDataManager: ObservableObject { return gym(withId: mostUsedGymId) } - func resetAllData() { + func resetAllData(showSuccessMessage: Bool = true) { gyms.removeAll() problems.removeAll() sessions.removeAll() @@ -490,8 +530,11 @@ class ClimbingDataManager: ObservableObject { userDefaults.removeObject(forKey: Keys.activeSession) DataStateManager.shared.reset() - successMessage = "All data has been reset" - clearMessageAfterDelay() + + if showSuccessMessage { + successMessage = "All data has been reset" + clearMessageAfterDelay() + } } func exportData() -> Data? { @@ -530,7 +573,7 @@ class ClimbingDataManager: ObservableObject { } } - func importData(from data: Data) throws { + func importData(from data: Data, showSuccessMessage: Bool = true) throws { do { let importResult = try ZipUtils.extractImportZip(data: data) @@ -566,7 +609,7 @@ class ClimbingDataManager: ObservableObject { try validateImportData(importData) - resetAllData() + resetAllData(showSuccessMessage: showSuccessMessage) let updatedProblems = updateProblemImagePaths( problems: importData.problems, @@ -586,9 +629,11 @@ class ClimbingDataManager: ObservableObject { // Update data state to current time since we just imported new data DataStateManager.shared.updateDataState() - successMessage = - "Data imported successfully with \(importResult.imagePathMapping.count) images" - clearMessageAfterDelay() + if showSuccessMessage { + successMessage = + "Data imported successfully with \(importResult.imagePathMapping.count) images" + clearMessageAfterDelay() + } } catch { setError("Import failed: \(error.localizedDescription)") throw error diff --git a/ios/OpenClimb/Views/AnalyticsView.swift b/ios/OpenClimb/Views/AnalyticsView.swift index 4d4874b..fa1aac1 100644 --- a/ios/OpenClimb/Views/AnalyticsView.swift +++ b/ios/OpenClimb/Views/AnalyticsView.swift @@ -20,6 +20,27 @@ struct AnalyticsView: View { .padding() } .navigationTitle("Analytics") + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + if dataManager.isSyncing { + HStack(spacing: 2) { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .blue)) + .scaleEffect(0.6) + } + .padding(.horizontal, 6) + .padding(.vertical, 3) + .background( + Circle() + .fill(.regularMaterial) + ) + .transition(.scale.combined(with: .opacity)) + .animation( + .easeInOut(duration: 0.2), value: dataManager.isSyncing + ) + } + } + } } } } diff --git a/ios/OpenClimb/Views/GymsView.swift b/ios/OpenClimb/Views/GymsView.swift index 31c895a..3937ae5 100644 --- a/ios/OpenClimb/Views/GymsView.swift +++ b/ios/OpenClimb/Views/GymsView.swift @@ -15,7 +15,25 @@ struct GymsView: View { } .navigationTitle("Gyms") .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItemGroup(placement: .navigationBarTrailing) { + if dataManager.isSyncing { + HStack(spacing: 2) { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .blue)) + .scaleEffect(0.6) + } + .padding(.horizontal, 6) + .padding(.vertical, 3) + .background( + Circle() + .fill(.regularMaterial) + ) + .transition(.scale.combined(with: .opacity)) + .animation( + .easeInOut(duration: 0.2), value: dataManager.isSyncing + ) + } + Button("Add") { showingAddGym = true } diff --git a/ios/OpenClimb/Views/LiveActivityDebugView.swift b/ios/OpenClimb/Views/LiveActivityDebugView.swift index 708aef5..5d75989 100644 --- a/ios/OpenClimb/Views/LiveActivityDebugView.swift +++ b/ios/OpenClimb/Views/LiveActivityDebugView.swift @@ -1,9 +1,5 @@ // // LiveActivityDebugView.swift -// OpenClimb -// -// Created by Assistant on 2025-09-15. -// import SwiftUI diff --git a/ios/OpenClimb/Views/ProblemsView.swift b/ios/OpenClimb/Views/ProblemsView.swift index da4bd6b..a4458f4 100644 --- a/ios/OpenClimb/Views/ProblemsView.swift +++ b/ios/OpenClimb/Views/ProblemsView.swift @@ -62,7 +62,25 @@ struct ProblemsView: View { .navigationTitle("Problems") .searchable(text: $searchText, prompt: "Search problems...") .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItemGroup(placement: .navigationBarTrailing) { + if dataManager.isSyncing { + HStack(spacing: 2) { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .blue)) + .scaleEffect(0.6) + } + .padding(.horizontal, 6) + .padding(.vertical, 3) + .background( + Circle() + .fill(.regularMaterial) + ) + .transition(.scale.combined(with: .opacity)) + .animation( + .easeInOut(duration: 0.2), value: dataManager.isSyncing + ) + } + if !dataManager.gyms.isEmpty { Button("Add") { showingAddProblem = true diff --git a/ios/OpenClimb/Views/SessionsView.swift b/ios/OpenClimb/Views/SessionsView.swift index 0d759d3..898d458 100644 --- a/ios/OpenClimb/Views/SessionsView.swift +++ b/ios/OpenClimb/Views/SessionsView.swift @@ -17,7 +17,25 @@ struct SessionsView: View { .navigationTitle("Sessions") .navigationBarTitleDisplayMode(.automatic) .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { + ToolbarItemGroup(placement: .navigationBarTrailing) { + if dataManager.isSyncing { + HStack(spacing: 2) { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .blue)) + .scaleEffect(0.6) + } + .padding(.horizontal, 6) + .padding(.vertical, 3) + .background( + Circle() + .fill(.regularMaterial) + ) + .transition(.scale.combined(with: .opacity)) + .animation( + .easeInOut(duration: 0.2), value: dataManager.isSyncing + ) + } + if dataManager.gyms.isEmpty { EmptyView() } else if dataManager.activeSession == nil { diff --git a/ios/OpenClimb/Views/SettingsView.swift b/ios/OpenClimb/Views/SettingsView.swift index 56a2694..cd55dd4 100644 --- a/ios/OpenClimb/Views/SettingsView.swift +++ b/ios/OpenClimb/Views/SettingsView.swift @@ -22,6 +22,27 @@ struct SettingsView: View { AppInfoSection() } .navigationTitle("Settings") + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + if dataManager.isSyncing { + HStack(spacing: 2) { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .blue)) + .scaleEffect(0.6) + } + .padding(.horizontal, 6) + .padding(.vertical, 3) + .background( + Circle() + .fill(.regularMaterial) + ) + .transition(.scale.combined(with: .opacity)) + .animation( + .easeInOut(duration: 0.2), value: dataManager.isSyncing + ) + } + } + } .sheet( item: Binding( get: { activeSheet }, @@ -436,6 +457,7 @@ struct SyncSection: View { .foregroundColor(.red) .padding(.leading, 24) } + } } .sheet(isPresented: $showingSyncSettings) { diff --git a/ios/SessionStatusLive/AppIntent.swift b/ios/SessionStatusLive/AppIntent.swift index 1efec63..1037e05 100644 --- a/ios/SessionStatusLive/AppIntent.swift +++ b/ios/SessionStatusLive/AppIntent.swift @@ -1,12 +1,8 @@ // // AppIntent.swift -// SessionStatusLive -// -// Created by Atridad Lahiji on 2025-09-15. -// -import WidgetKit import AppIntents +import WidgetKit struct ConfigurationAppIntent: WidgetConfigurationIntent { static var title: LocalizedStringResource { "Configuration" } diff --git a/ios/SessionStatusLive/SessionStatusLive.swift b/ios/SessionStatusLive/SessionStatusLive.swift index 28fc8b4..89bc119 100644 --- a/ios/SessionStatusLive/SessionStatusLive.swift +++ b/ios/SessionStatusLive/SessionStatusLive.swift @@ -1,9 +1,5 @@ // // SessionStatusLive.swift -// SessionStatusLive -// -// Created by Atridad Lahiji on 2025-09-15. -// import SwiftUI import WidgetKit diff --git a/ios/SessionStatusLive/SessionStatusLiveBundle.swift b/ios/SessionStatusLive/SessionStatusLiveBundle.swift index f632dd5..34e1bef 100644 --- a/ios/SessionStatusLive/SessionStatusLiveBundle.swift +++ b/ios/SessionStatusLive/SessionStatusLiveBundle.swift @@ -1,12 +1,8 @@ // // SessionStatusLiveBundle.swift -// SessionStatusLive -// -// Created by Atridad Lahiji on 2025-09-15. -// -import WidgetKit import SwiftUI +import WidgetKit @main struct SessionStatusLiveBundle: WidgetBundle { diff --git a/ios/SessionStatusLive/SessionStatusLiveControl.swift b/ios/SessionStatusLive/SessionStatusLiveControl.swift index cd7f525..70f5ae6 100644 --- a/ios/SessionStatusLive/SessionStatusLiveControl.swift +++ b/ios/SessionStatusLive/SessionStatusLiveControl.swift @@ -1,9 +1,5 @@ // // SessionStatusLiveControl.swift -// SessionStatusLive -// -// Created by Atridad Lahiji on 2025-09-15. -// import AppIntents import SwiftUI @@ -42,8 +38,9 @@ extension SessionStatusLiveControl { } func currentValue(configuration: TimerConfiguration) async throws -> Value { - let isRunning = true // Check if the timer is running - return SessionStatusLiveControl.Value(isRunning: isRunning, name: configuration.timerName) + let isRunning = true // Check if the timer is running + return SessionStatusLiveControl.Value( + isRunning: isRunning, name: configuration.timerName) } } } diff --git a/ios/SessionStatusLive/SessionStatusLiveLiveActivity.swift b/ios/SessionStatusLive/SessionStatusLiveLiveActivity.swift index 7daadc0..3725c0a 100644 --- a/ios/SessionStatusLive/SessionStatusLiveLiveActivity.swift +++ b/ios/SessionStatusLive/SessionStatusLiveLiveActivity.swift @@ -1,9 +1,5 @@ // // SessionStatusLiveLiveActivity.swift -// SessionStatusLive -// -// Created by Atridad Lahiji on 2025-09-15. -// import ActivityKit import SwiftUI