diff --git a/xcode/Catnip.xcodeproj/project.pbxproj b/xcode/Catnip.xcodeproj/project.pbxproj
index 9fc20ef8..f4376703 100644
--- a/xcode/Catnip.xcodeproj/project.pbxproj
+++ b/xcode/Catnip.xcodeproj/project.pbxproj
@@ -423,9 +423,10 @@
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 = QCUUU7QVF7;
+ DEVELOPMENT_TEAM = 5DTHBP38WM;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = CatnipWidgets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = CatnipWidgets;
@@ -437,8 +438,9 @@
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = wandb.catnip.CatnipWidgets;
+ 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,9 +458,10 @@
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 = QCUUU7QVF7;
+ DEVELOPMENT_TEAM = 5DTHBP38WM;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = CatnipWidgets/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = CatnipWidgets;
@@ -470,8 +473,9 @@
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = wandb.catnip.CatnipWidgets;
+ 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,12 +494,15 @@
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 = 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,8 +513,9 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = wandb.catnip;
+ 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;
@@ -524,12 +532,15 @@
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 = 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,8 +551,9 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = wandb.catnip;
+ 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;
@@ -678,7 +690,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 +711,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 +731,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 +750,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..cf03a97a 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,14 @@
NSUserNotificationsUsageDescription
Catnip sends notifications when your codespace is ready. Creating a codespace can take up to 10 minutes.
+ ITSAppUsesNonExemptEncryption
+
+ NSSupportsLiveActivities
+
+ UIBackgroundModes
+
+ remote-notification
+ fetch
+
diff --git a/xcode/catnip/Views/CodespaceView.swift b/xcode/catnip/Views/CodespaceView.swift
index 7d51f99e..4f4c0720 100644
--- a/xcode/catnip/Views/CodespaceView.swift
+++ b/xcode/catnip/Views/CodespaceView.swift
@@ -26,6 +26,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
@@ -40,6 +41,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.",
@@ -227,6 +230,10 @@ struct CodespaceView: View {
NSLog("🐱 [CodespaceView] Failed to preload repositories: \(error)")
}
}
+
+ }
+ .onChange(of: scenePhase) { oldPhase, newPhase in
+ handleScenePhaseChange(oldPhase: oldPhase, newPhase: newPhase)
}
}
@@ -510,6 +517,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")
@@ -1115,6 +1125,31 @@ struct CodespaceView: View {
.background(Color(uiColor: .systemGroupedBackground))
}
+ // MARK: - App Lifecycle Handling
+
+ 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")
+ }
+
+ // 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
+ sseService?.disconnect()
+ sseService = nil
+
+ // Restart the connection with the same codespace name
+ handleConnect(codespaceName: pendingCodespaceName)
+
+ // Reset the flag
+ wasConnectingBeforeBackground = false
+ }
+ }
+
private var createRepositoryView: some View {
ScrollView {
VStack(spacing: 24) {
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