From 4633c7253a27bea351f21eba60c2b735d2e2d38c Mon Sep 17 00:00:00 2001 From: zarzet Date: Sun, 1 Feb 2026 21:14:45 +0700 Subject: [PATCH] fix(ios): block iCloud Drive folder selection - Detect iCloud path and show error when user tries to select it - Fallback to app Documents folder if iCloud path detected at runtime - Add localization string for iCloud not supported error --- CHANGELOG.md | 1 + lib/l10n/app_localizations.dart | 6 ++++++ lib/l10n/app_localizations_de.dart | 4 ++++ lib/l10n/app_localizations_en.dart | 4 ++++ lib/l10n/app_localizations_es.dart | 4 ++++ lib/l10n/app_localizations_fr.dart | 4 ++++ lib/l10n/app_localizations_hi.dart | 4 ++++ lib/l10n/app_localizations_id.dart | 4 ++++ lib/l10n/app_localizations_ja.dart | 4 ++++ lib/l10n/app_localizations_ko.dart | 4 ++++ lib/l10n/app_localizations_nl.dart | 4 ++++ lib/l10n/app_localizations_pt.dart | 4 ++++ lib/l10n/app_localizations_ru.dart | 4 ++++ lib/l10n/app_localizations_tr.dart | 4 ++++ lib/l10n/app_localizations_zh.dart | 4 ++++ lib/l10n/arb/app_en.arb | 4 +++- lib/providers/download_queue_provider.dart | 19 +++++++++++++++++- .../settings/download_settings_page.dart | 20 ++++++++++++++++++- 18 files changed, 99 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c6b0fc8..a6b9e732 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Same as 3.3.1 but fixes crash issues caused by FFmpeg. - **FFmpeg Crash**: Fixed crash issues during M4A to MP3/Opus conversion - **Service Selection Ignored**: Fixed bug where selecting Qobuz/Amazon from service picker was ignored and always used Tidal instead +- **iOS iCloud Drive Permission Error**: Block iCloud Drive folder selection on iOS (Go backend cannot access iCloud due to sandboxing) ### Changed diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index a9d694ce..4e1f9cdd 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1312,6 +1312,12 @@ abstract class AppLocalizations { /// **'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'** String get setupIosEmptyFolderWarning; + /// Error when user selects iCloud Drive on iOS + /// + /// In en, this message translates to: + /// **'iCloud Drive is not supported. Please use the app Documents folder.'** + String get setupIcloudNotSupported; + /// App tagline in setup /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 0ef84182..2caf7511 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -698,6 +698,10 @@ class AppLocalizationsDe extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS-Einschränkung: Leere Ordner können nicht ausgewählt werden. Wählen Sie einen Ordner mit mindestens einer Datei.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Spotify Titel in FLAC herunterladen'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 4d4b9fd6..f8e8bab4 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -684,6 +684,10 @@ class AppLocalizationsEn extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Download Spotify tracks in FLAC'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 0779ebfa..d535699e 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -684,6 +684,10 @@ class AppLocalizationsEs extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Download Spotify tracks in FLAC'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index dee4eb55..f8a9dc92 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -684,6 +684,10 @@ class AppLocalizationsFr extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Download Spotify tracks in FLAC'; diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart index 93b0dd82..d3c940a3 100644 --- a/lib/l10n/app_localizations_hi.dart +++ b/lib/l10n/app_localizations_hi.dart @@ -684,6 +684,10 @@ class AppLocalizationsHi extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Download Spotify tracks in FLAC'; diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index 336d24fc..88b145ba 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -689,6 +689,10 @@ class AppLocalizationsId extends AppLocalizations { String get setupIosEmptyFolderWarning => 'Batasan iOS: Folder kosong tidak dapat dipilih. Pilih folder dengan minimal satu file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Unduh lagu Spotify dalam format FLAC'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 3b2c833e..ee68b1a4 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -679,6 +679,10 @@ class AppLocalizationsJa extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Spotify のトラックを FLAC でダウンロード'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index d257a8de..ec860eae 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -684,6 +684,10 @@ class AppLocalizationsKo extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Download Spotify tracks in FLAC'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 600ab6c2..70fe43bf 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -684,6 +684,10 @@ class AppLocalizationsNl extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Download Spotify tracks in FLAC'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index 941fa8b7..407cf3cf 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -684,6 +684,10 @@ class AppLocalizationsPt extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Download Spotify tracks in FLAC'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index f9cfd3bd..ea4d5536 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -702,6 +702,10 @@ class AppLocalizationsRu extends AppLocalizations { String get setupIosEmptyFolderWarning => 'Ограничение iOS: пустые папки не могут быть выбраны. Выберите папку, содержащую хотя бы один файл.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Скачать Spotify треки во FLAC'; diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart index 5880f0ee..d77dd0cc 100644 --- a/lib/l10n/app_localizations_tr.dart +++ b/lib/l10n/app_localizations_tr.dart @@ -691,6 +691,10 @@ class AppLocalizationsTr extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS\'un sınırlaması: Boş klasörler seçilemiyor. İçinde en az bir dosya bulunan bir klasör seçin.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Spotify şarkılarını FLAC olarak indirin'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 3083e987..6e721398 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -684,6 +684,10 @@ class AppLocalizationsZh extends AppLocalizations { String get setupIosEmptyFolderWarning => 'iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.'; + @override + String get setupIcloudNotSupported => + 'iCloud Drive is not supported. Please use the app Documents folder.'; + @override String get setupDownloadInFlac => 'Download Spotify tracks in FLAC'; diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index d93e464f..6b798025 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -481,8 +481,10 @@ "@setupChooseFromFiles": {"description": "iOS file picker option"}, "setupChooseFromFilesSubtitle": "Select iCloud or other location", "@setupChooseFromFilesSubtitle": {"description": "Subtitle for file picker"}, - "setupIosEmptyFolderWarning": "iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.", +"setupIosEmptyFolderWarning": "iOS limitation: Empty folders cannot be selected. Choose a folder with at least one file.", "@setupIosEmptyFolderWarning": {"description": "iOS folder selection warning"}, + "setupIcloudNotSupported": "iCloud Drive is not supported. Please use the app Documents folder.", + "@setupIcloudNotSupported": {"description": "Error when user selects iCloud Drive on iOS"}, "setupDownloadInFlac": "Download Spotify tracks in FLAC", "@setupDownloadInFlac": {"description": "App tagline in setup"}, "setupStepStorage": "Storage", diff --git a/lib/providers/download_queue_provider.dart b/lib/providers/download_queue_provider.dart index 678e7837..950aebbe 100644 --- a/lib/providers/download_queue_provider.dart +++ b/lib/providers/download_queue_provider.dart @@ -1564,11 +1564,28 @@ class DownloadQueueNotifier extends Notifier { } } - if (state.outputDir.isEmpty) { +if (state.outputDir.isEmpty) { _log.d('Output dir empty, initializing...'); await _initOutputDir(); } + // iOS: Validate that outputDir is writable (not iCloud Drive which Go can't access) + if (Platform.isIOS && state.outputDir.isNotEmpty) { + final isICloudPath = state.outputDir.contains('Mobile Documents') || + state.outputDir.contains('CloudDocs') || + state.outputDir.contains('com~apple~CloudDocs'); + if (isICloudPath) { + _log.w('iOS: iCloud Drive path detected, falling back to app Documents folder'); + _log.w('Go backend cannot write to iCloud Drive due to iOS sandboxing'); + final dir = await getApplicationDocumentsDirectory(); + final musicDir = Directory('${dir.path}/SpotiFLAC'); + if (!await musicDir.exists()) { + await musicDir.create(recursive: true); + } + state = state.copyWith(outputDir: musicDir.path); + } + } + if (state.outputDir.isEmpty) { _log.d('Using fallback directory...'); final dir = await getApplicationDocumentsDirectory(); diff --git a/lib/screens/settings/download_settings_page.dart b/lib/screens/settings/download_settings_page.dart index ab4c428e..eb562bba 100644 --- a/lib/screens/settings/download_settings_page.dart +++ b/lib/screens/settings/download_settings_page.dart @@ -702,7 +702,7 @@ class _DownloadSettingsPageState extends ConsumerState { if (ctx.mounted) Navigator.pop(ctx); }, ), - ListTile( +ListTile( leading: Icon(Icons.cloud, color: colorScheme.onSurfaceVariant), title: Text(context.l10n.setupChooseFromFiles), subtitle: Text(context.l10n.setupChooseFromFilesSubtitle), @@ -711,6 +711,24 @@ class _DownloadSettingsPageState extends ConsumerState { // Note: iOS requires folder to have at least one file to be selectable final result = await FilePicker.platform.getDirectoryPath(); if (result != null) { + // iOS: Check if user selected iCloud Drive (not accessible by Go backend) + if (Platform.isIOS) { + final isICloudPath = result.contains('Mobile Documents') || + result.contains('CloudDocs') || + result.contains('com~apple~CloudDocs'); + if (isICloudPath) { + if (ctx.mounted) { + ScaffoldMessenger.of(ctx).showSnackBar( + SnackBar( + content: Text(context.l10n.setupIcloudNotSupported), + backgroundColor: Theme.of(ctx).colorScheme.error, + duration: const Duration(seconds: 4), + ), + ); + } + return; + } + } ref .read(settingsProvider.notifier) .setDownloadDirectory(result);