fix(watch-connectivity): improve WCSession lifecycle and message reliability

This commit is contained in:
2026-08-07 15:32:40 +02:00
parent e64e2353d9
commit 00a8220096
8 changed files with 266 additions and 71 deletions
@@ -23,8 +23,9 @@ export function PreSwimScreen() {
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
const route = useRoute<RouteProp<RootStackParamList, "PreSwim">>();
const [workout, setWorkout] = useState<WorkoutWithSets | null>(null);
const [syncResult, setSyncResult] = useState<"sent" | "error" | null>(null);
const { isSyncing, sendWorkoutToWatch } = useSyncWorkout();
const { status, isPaired, isInstalled } = useWatchConnection();
const { status, isPaired, isInstalled, isReady } = useWatchConnection();
useEffect(() => {
loadWorkout();
@@ -38,7 +39,7 @@ export function PreSwimScreen() {
const handleStartSwim = async () => {
if (!workout) return;
if (status !== "connected") {
if (!isReady) {
navigation.navigate("PostSwim", { workoutId: route.params.workoutId });
return;
}
@@ -51,11 +52,11 @@ export function PreSwimScreen() {
);
if ("error" in result) {
navigation.navigate("PostSwim", { workoutId: route.params.workoutId });
setSyncResult("error");
return;
}
navigation.navigate("PostSwim", { workoutId: route.params.workoutId });
setSyncResult("sent");
};
if (!workout) {
@@ -66,6 +67,46 @@ export function PreSwimScreen() {
);
}
if (syncResult === "sent") {
return (
<View className="flex-1 bg-white p-4 items-center justify-center">
<View className="bg-green-50 rounded-2xl p-6 items-center mb-6 w-full">
<Text className="text-4xl mb-3">⌚</Text>
<Text className="text-xl font-bold text-green-800 mb-2">Workout sent to Watch!</Text>
<Text className="text-green-600 text-center">
Open the SwimBuddy app on your Apple Watch to start tracking.
</Text>
</View>
<Pressable
onPress={() => navigation.popToTop()}
className="bg-slate-900 rounded-2xl py-4 items-center w-full"
>
<Text className="text-white font-bold text-lg">Done</Text>
</Pressable>
</View>
);
}
if (syncResult === "error") {
return (
<View className="flex-1 bg-white p-4 items-center justify-center">
<View className="bg-red-50 rounded-2xl p-6 items-center mb-6 w-full">
<Text className="text-4xl mb-3">⚠️</Text>
<Text className="text-xl font-bold text-red-800 mb-2">Failed to send</Text>
<Text className="text-red-600 text-center">
Could not send workout to Watch. Starting without Watch instead.
</Text>
</View>
<Pressable
onPress={() => navigation.navigate("PostSwim", { workoutId: route.params.workoutId })}
className="bg-sky-500 rounded-2xl py-4 items-center w-full"
>
<Text className="text-white font-bold text-lg">Start Without Watch</Text>
</Pressable>
</View>
);
}
return (
<View className="flex-1 bg-white p-4">
<View className="items-center mb-6">
@@ -112,14 +153,16 @@ export function PreSwimScreen() {
<View className="bg-slate-50 rounded-2xl p-3 mb-6 flex-row items-center gap-3">
<View
className={`w-3 h-3 rounded-full ${
status === "connected" ? "bg-green-500" : "bg-slate-300"
isReady ? "bg-green-500" : isPaired ? "bg-yellow-500" : "bg-slate-300"
}`}
/>
<Text className="text-sm text-slate-600">
{status === "connected"
? "Apple Watch connected"
{isReady
? "Apple Watch ready"
: isPaired
? "Watch paired but not connected"
? isInstalled
? "Watch paired but not in range"
: "Watch paired, app not installed"
: "No Watch paired"}
</Text>
</View>
@@ -135,14 +178,14 @@ export function PreSwimScreen() {
<ActivityIndicator color="#fff" />
) : (
<Text className="text-white font-bold text-lg">
{status === "connected" ? "Send to Watch & Start" : "Start Without Watch"}
{isReady ? "Send to Watch & Start" : "Start Without Watch"}
</Text>
)}
</Pressable>
<Text className="text-slate-400 text-xs text-center mt-3">
{status === "connected"
? "Workout will be sent to your Apple Watch"
{isReady
? "Workout will be synced to your Apple Watch"
: "Workout will be tracked on this device"}
</Text>
</View>