From d9fdc6e10a9dc28ae6730b2807956522035c3a0c Mon Sep 17 00:00:00 2001 From: "Chris Van Pelt (CVP)" Date: Sun, 9 Nov 2025 03:06:57 +0000 Subject: [PATCH 1/6] SSE Reconnection on Foreground checkpoint: 1 --- xcode/catnip/Views/CodespaceView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xcode/catnip/Views/CodespaceView.swift b/xcode/catnip/Views/CodespaceView.swift index 156a3d58..1d02d94b 100644 --- a/xcode/catnip/Views/CodespaceView.swift +++ b/xcode/catnip/Views/CodespaceView.swift @@ -39,6 +39,8 @@ struct CodespaceView: View { @State private var createdCodespace: CodespaceCreationResult.CodespaceInfo? @State private var repositoryListMode: RepositoryListMode = .installation @State private var pendingRepository: String? + @State private var pendingCodespaceName: String? + @State private var wasConnectingBeforeBackground = false private let catFacts = [ "Cats can rotate their ears 180 degrees.", From 495daddc9ddc7df5b23dd4eb10e213ee949c5c7b Mon Sep 17 00:00:00 2001 From: "Chris Van Pelt (CVP)" Date: Sun, 9 Nov 2025 03:07:28 +0000 Subject: [PATCH 2/6] SSE Reconnection on Foreground checkpoint: 2 --- xcode/catnip/Views/CodespaceView.swift | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/xcode/catnip/Views/CodespaceView.swift b/xcode/catnip/Views/CodespaceView.swift index 1d02d94b..3cbcc237 100644 --- a/xcode/catnip/Views/CodespaceView.swift +++ b/xcode/catnip/Views/CodespaceView.swift @@ -206,6 +206,12 @@ struct CodespaceView: View { NSLog("🐱 [CodespaceView] Failed to preload repositories: \(error)") } } + + // Set up app lifecycle observers for SSE reconnection + setupLifecycleObservers() + } + .onDisappear { + removeLifecycleObservers() } } @@ -489,6 +495,9 @@ struct CodespaceView: View { statusMessage = "" statusMessage = "Finding your codespace..." + // Store codespace name for potential reconnection after backgrounding + pendingCodespaceName = codespaceName + // Mock connection for UI tests if UITestingHelper.isUITesting { UserDefaults.standard.set("mock-codespace", forKey: "codespace_name") @@ -1042,6 +1051,64 @@ struct CodespaceView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color(uiColor: .systemGroupedBackground)) } + + // MARK: - App Lifecycle Observers + + private func setupLifecycleObservers() { + NotificationCenter.default.addObserver( + forName: UIApplication.willEnterForegroundNotification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.handleAppWillEnterForeground() + } + + NotificationCenter.default.addObserver( + forName: UIApplication.didEnterBackgroundNotification, + object: nil, + queue: .main + ) { [weak self] _ in + self?.handleAppDidEnterBackground() + } + } + + private func removeLifecycleObservers() { + NotificationCenter.default.removeObserver( + self, + name: UIApplication.willEnterForegroundNotification, + object: nil + ) + NotificationCenter.default.removeObserver( + self, + name: UIApplication.didEnterBackgroundNotification, + object: nil + ) + } + + private func handleAppDidEnterBackground() { + // Remember if we were connecting when we went to background + if phase == .connecting { + wasConnectingBeforeBackground = true + NSLog("🐱 [CodespaceView] App backgrounded during SSE connection, will reconnect on foreground") + } + } + + private func handleAppWillEnterForeground() { + // Reconnect if we were connecting when backgrounded + if wasConnectingBeforeBackground && phase == .connecting { + NSLog("🐱 [CodespaceView] App foregrounded, reconnecting SSE...") + + // Disconnect the old stale connection + sseService?.disconnect() + sseService = nil + + // Restart the connection with the same codespace name + handleConnect(codespaceName: pendingCodespaceName) + + // Reset the flag + wasConnectingBeforeBackground = false + } + } } // MARK: - Previews From a646485748604f7e14ab8f2c076d73fdf04dace6 Mon Sep 17 00:00:00 2001 From: "Chris Van Pelt (CVP)" Date: Sun, 9 Nov 2025 03:08:28 +0000 Subject: [PATCH 3/6] SSE Reconnection on Foreground checkpoint: 3 --- xcode/catnip/Views/CodespaceView.swift | 52 +++++--------------------- 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/xcode/catnip/Views/CodespaceView.swift b/xcode/catnip/Views/CodespaceView.swift index 3cbcc237..b807a755 100644 --- a/xcode/catnip/Views/CodespaceView.swift +++ b/xcode/catnip/Views/CodespaceView.swift @@ -25,6 +25,7 @@ enum RepositoryListMode { } struct CodespaceView: View { + @Environment(\.scenePhase) private var scenePhase @EnvironmentObject var authManager: AuthManager @StateObject private var installer = CatnipInstaller.shared @StateObject private var tracker = CodespaceCreationTracker.shared @@ -207,11 +208,9 @@ struct CodespaceView: View { } } - // Set up app lifecycle observers for SSE reconnection - setupLifecycleObservers() } - .onDisappear { - removeLifecycleObservers() + .onChange(of: scenePhase) { oldPhase, newPhase in + handleScenePhaseChange(oldPhase: oldPhase, newPhase: newPhase) } } @@ -1052,50 +1051,17 @@ struct CodespaceView: View { .background(Color(uiColor: .systemGroupedBackground)) } - // MARK: - App Lifecycle Observers + // MARK: - App Lifecycle Handling - private func setupLifecycleObservers() { - NotificationCenter.default.addObserver( - forName: UIApplication.willEnterForegroundNotification, - object: nil, - queue: .main - ) { [weak self] _ in - self?.handleAppWillEnterForeground() - } - - NotificationCenter.default.addObserver( - forName: UIApplication.didEnterBackgroundNotification, - object: nil, - queue: .main - ) { [weak self] _ in - self?.handleAppDidEnterBackground() - } - } - - private func removeLifecycleObservers() { - NotificationCenter.default.removeObserver( - self, - name: UIApplication.willEnterForegroundNotification, - object: nil - ) - NotificationCenter.default.removeObserver( - self, - name: UIApplication.didEnterBackgroundNotification, - object: nil - ) - } - - private func handleAppDidEnterBackground() { - // Remember if we were connecting when we went to background - if phase == .connecting { + private func handleScenePhaseChange(oldPhase: ScenePhase, newPhase: ScenePhase) { + // Track when app goes to background during SSE connection + if newPhase == .background && phase == .connecting { wasConnectingBeforeBackground = true NSLog("🐱 [CodespaceView] App backgrounded during SSE connection, will reconnect on foreground") } - } - private func handleAppWillEnterForeground() { - // Reconnect if we were connecting when backgrounded - if wasConnectingBeforeBackground && phase == .connecting { + // Reconnect when app returns to foreground if we were connecting + if newPhase == .active && oldPhase == .background && wasConnectingBeforeBackground && phase == .connecting { NSLog("🐱 [CodespaceView] App foregrounded, reconnecting SSE...") // Disconnect the old stale connection From 22b418c4550d0733dfea9e89bbea47675a966f98 Mon Sep 17 00:00:00 2001 From: "Chris Van Pelt (CVP)" Date: Sun, 9 Nov 2025 03:18:42 +0000 Subject: [PATCH 4/6] Merge origin/main into catnip/misty --- xcode/catnip/Views/CodespaceView.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/xcode/catnip/Views/CodespaceView.swift b/xcode/catnip/Views/CodespaceView.swift index 228a42a8..4f4c0720 100644 --- a/xcode/catnip/Views/CodespaceView.swift +++ b/xcode/catnip/Views/CodespaceView.swift @@ -1125,7 +1125,6 @@ struct CodespaceView: View { .background(Color(uiColor: .systemGroupedBackground)) } -<<<<<<< HEAD // MARK: - App Lifecycle Handling private func handleScenePhaseChange(oldPhase: ScenePhase, newPhase: ScenePhase) { @@ -1149,7 +1148,8 @@ struct CodespaceView: View { // Reset the flag wasConnectingBeforeBackground = false } -======= + } + private var createRepositoryView: some View { ScrollView { VStack(spacing: 24) { @@ -1245,7 +1245,6 @@ struct CodespaceView: View { } .scrollBounceBehavior(.basedOnSize) .background(Color(uiColor: .systemGroupedBackground)) ->>>>>>> origin/main } } From 814a0b608fcedd57cc11d60571cdf68862f6222d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Nov 2025 17:39:44 -0500 Subject: [PATCH 5/6] Update catnip to new apple team --- xcode/Catnip.xcodeproj/project.pbxproj | 28 +++++++++++++---------- xcode/CatnipWidgetsExtension.entitlements | 2 +- xcode/catnip/Info.plist | 14 ++++-------- xcode/catnip/catnip.entitlements | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/xcode/Catnip.xcodeproj/project.pbxproj b/xcode/Catnip.xcodeproj/project.pbxproj index 9fc20ef8..38c63878 100644 --- a/xcode/Catnip.xcodeproj/project.pbxproj +++ b/xcode/Catnip.xcodeproj/project.pbxproj @@ -425,7 +425,7 @@ CODE_SIGN_ENTITLEMENTS = CatnipWidgetsExtension.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = QCUUU7QVF7; + DEVELOPMENT_TEAM = 5DTHBP38WM; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = CatnipWidgets/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = CatnipWidgets; @@ -437,7 +437,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = wandb.catnip.CatnipWidgets; + PRODUCT_BUNDLE_IDENTIFIER = com.wandb.catnip.CatnipWidgets; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -458,7 +458,7 @@ CODE_SIGN_ENTITLEMENTS = CatnipWidgetsExtension.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = QCUUU7QVF7; + DEVELOPMENT_TEAM = 5DTHBP38WM; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = CatnipWidgets/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = CatnipWidgets; @@ -470,7 +470,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = wandb.catnip.CatnipWidgets; + PRODUCT_BUNDLE_IDENTIFIER = com.wandb.catnip.CatnipWidgets; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -492,10 +492,12 @@ CODE_SIGN_ENTITLEMENTS = catnip/catnip.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; - DEVELOPMENT_TEAM = QCUUU7QVF7; + DEVELOPMENT_TEAM = 5DTHBP38WM; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = catnip/Info.plist; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; + INFOPLIST_KEY_NSSupportsLiveActivities = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -506,7 +508,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = wandb.catnip; + PRODUCT_BUNDLE_IDENTIFIER = com.wandb.catnip; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; @@ -526,10 +528,12 @@ CODE_SIGN_ENTITLEMENTS = catnip/catnip.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; - DEVELOPMENT_TEAM = QCUUU7QVF7; + DEVELOPMENT_TEAM = 5DTHBP38WM; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = catnip/Info.plist; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; + INFOPLIST_KEY_NSSupportsLiveActivities = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -540,7 +544,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = wandb.catnip; + PRODUCT_BUNDLE_IDENTIFIER = com.wandb.catnip; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; @@ -678,7 +682,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = QCUUU7QVF7; + DEVELOPMENT_TEAM = 5DTHBP38WM; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; MARKETING_VERSION = 1.0; @@ -699,7 +703,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = QCUUU7QVF7; + DEVELOPMENT_TEAM = 5DTHBP38WM; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; MARKETING_VERSION = 1.0; @@ -719,7 +723,7 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = QCUUU7QVF7; + DEVELOPMENT_TEAM = 5DTHBP38WM; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = wandb.catnipUITests; @@ -738,7 +742,7 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = QCUUU7QVF7; + DEVELOPMENT_TEAM = 5DTHBP38WM; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = wandb.catnipUITests; diff --git a/xcode/CatnipWidgetsExtension.entitlements b/xcode/CatnipWidgetsExtension.entitlements index aa653443..18ed62bc 100644 --- a/xcode/CatnipWidgetsExtension.entitlements +++ b/xcode/CatnipWidgetsExtension.entitlements @@ -4,7 +4,7 @@ com.apple.security.application-groups - group.com.wandb.catnip + group.com.wandb.catnip.widgets diff --git a/xcode/catnip/Info.plist b/xcode/catnip/Info.plist index b88ea579..5aab623a 100644 --- a/xcode/catnip/Info.plist +++ b/xcode/catnip/Info.plist @@ -2,15 +2,6 @@ - ITSAppUsesNonExemptEncryption - - NSSupportsLiveActivities - - UIBackgroundModes - - remote-notification - fetch - CFBundleURLTypes @@ -24,5 +15,10 @@ NSUserNotificationsUsageDescription Catnip sends notifications when your codespace is ready. Creating a codespace can take up to 10 minutes. + UIBackgroundModes + + remote-notification + fetch + diff --git a/xcode/catnip/catnip.entitlements b/xcode/catnip/catnip.entitlements index 3e80a236..5d5f9b30 100644 --- a/xcode/catnip/catnip.entitlements +++ b/xcode/catnip/catnip.entitlements @@ -6,7 +6,7 @@ development com.apple.security.application-groups - group.com.wandb.catnip + group.com.wandb.catnip.widgets From 4c8ef92d4250d8971d0acfe72236c7d2d2f4f7c6 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Nov 2025 23:55:34 -0500 Subject: [PATCH 6/6] Update xcode config --- xcode/Catnip.xcodeproj/project.pbxproj | 8 ++++++++ xcode/catnip/Info.plist | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/xcode/Catnip.xcodeproj/project.pbxproj b/xcode/Catnip.xcodeproj/project.pbxproj index 38c63878..f4376703 100644 --- a/xcode/Catnip.xcodeproj/project.pbxproj +++ b/xcode/Catnip.xcodeproj/project.pbxproj @@ -423,6 +423,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = CatnipWidgetsExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5DTHBP38WM; @@ -439,6 +440,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.wandb.catnip.CatnipWidgets; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; "SWIFT_ACTIVE_COMPILATION_CONDITIONS[arch=*]" = WIDGET_EXTENSION; @@ -456,6 +458,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = CatnipWidgetsExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5DTHBP38WM; @@ -472,6 +475,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.wandb.catnip.CatnipWidgets; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; "SWIFT_ACTIVE_COMPILATION_CONDITIONS[arch=*]" = WIDGET_EXTENSION; @@ -490,6 +494,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = catnip/catnip.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 5DTHBP38WM; @@ -510,6 +515,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.wandb.catnip; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_EMIT_LOC_STRINGS = YES; @@ -526,6 +532,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = catnip/catnip.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 5DTHBP38WM; @@ -546,6 +553,7 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.wandb.catnip; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/xcode/catnip/Info.plist b/xcode/catnip/Info.plist index 5aab623a..cf03a97a 100644 --- a/xcode/catnip/Info.plist +++ b/xcode/catnip/Info.plist @@ -15,6 +15,10 @@ NSUserNotificationsUsageDescription Catnip sends notifications when your codespace is ready. Creating a codespace can take up to 10 minutes. + ITSAppUsesNonExemptEncryption + + NSSupportsLiveActivities + UIBackgroundModes remote-notification