From 057fbb3c9ac6b3b6fd46f7f4fedc29e45c63ad39 Mon Sep 17 00:00:00 2001 From: Millian Lamiaux Date: Fri, 15 May 2026 23:51:51 +0200 Subject: [PATCH] fix: add 6s timeout to MusicService Supabase fetch for offline fallback When airplane mode is active, the Supabase client hung indefinitely waiting for a network response, blocking the mock track fallback. Now races the query against a 6-second Task.sleep so mock tracks load immediately after timeout. --- .../TabataGo/Services/MusicService.swift | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/tabatago-swift/TabataGo/Services/MusicService.swift b/tabatago-swift/TabataGo/Services/MusicService.swift index 957c2f0..f820646 100644 --- a/tabatago-swift/TabataGo/Services/MusicService.swift +++ b/tabatago-swift/TabataGo/Services/MusicService.swift @@ -50,9 +50,6 @@ actor MusicService { // ─── Supabase Fetch ────────────────────────────────────────── private func fetchFromSupabase(vibe: MusicVibe) async -> [MusicTrack]? { - // Re-use the existing SupabaseService's client configuration. - // We build our own client here because SupabaseService is an actor - // and doesn't expose the raw client. guard !AppEnvironment.isPreview else { return nil } let urlRaw = Bundle.main.infoDictionary?["SUPABASE_URL"] as? String ?? "" @@ -63,16 +60,30 @@ actor MusicService { } let client = SupabaseClient(supabaseURL: url, supabaseKey: key) + let genres = vibe.genres do { - let rows: [DownloadItemRow] = try await client - .from("download_items") - .select("id, video_id, title, duration_seconds, public_url, storage_path, genre") - .eq("status", value: "completed") - .in("genre", values: vibe.genres) - .limit(50) - .execute() - .value + let rows: [DownloadItemRow] = try await withThrowingTaskGroup(of: [DownloadItemRow].self) { group in + group.addTask { + try await client + .from("download_items") + .select("id, video_id, title, duration_seconds, public_url, storage_path, genre") + .eq("status", value: "completed") + .in("genre", values: genres) + .limit(50) + .execute() + .value + } + group.addTask { + try await Task.sleep(for: .seconds(6)) + throw CancellationError() + } + guard let result = try await group.next() else { + return [] + } + group.cancelAll() + return result + } let tracks: [MusicTrack] = rows.compactMap { row in guard let trackURL = row.resolvedURL(supabaseBase: urlRaw) else { return nil }