From ca9118cf47901c17bbd067c5a5fbe3e45ea34483 Mon Sep 17 00:00:00 2001 From: gaetano <64538010+skrtdev@users.noreply.github.com> Date: Thu, 21 May 2026 13:04:01 +0200 Subject: [PATCH] fix(ios): avoid TCC prompt on Mac Catalyst by using Application Support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Mac Catalyst, the app process is unsandboxed and NSDocumentDirectory resolves to the user's ~/Documents folder. The first time the app touches anything inside that folder, the macOS TCC subsystem shows a "App would like to access files in your Documents folder" prompt — which fires the moment the database is opened at launch, before any UI is on screen. NSApplicationSupportDirectory resolves to ~/Library/Application Support, which is per-app, private to the bundle, and exempt from TCC. Storing the WatermelonDB SQLite file under //.db keeps Mac Catalyst users from being prompted at every cold launch. Behaviour on iOS and iPadOS is unchanged (the new branch is gated to `#if TARGET_OS_MACCATALYST`). We intentionally do not migrate from the legacy ~/Documents path: probing for a legacy file would itself trigger the TCC prompt we are trying to avoid. Catalyst-only apps starting fresh get a clean location; existing iOS apps that ship a Catalyst build alongside keep their iOS database at ~/Documents and a separate Catalyst database under Application Support. Both location helpers are touched: - DatabasePlatformIOS.mm::resolveDatabasePath (C++ entry point) - WMDatabaseDriver.m::pathForName (Obj-C driver entry point) --- .../ios/WatermelonDB/DatabasePlatformIOS.mm | 34 +++++++++++++++++++ .../ios/WatermelonDB/objc/WMDatabaseDriver.m | 24 +++++++++++++ 2 files changed, 58 insertions(+) diff --git a/native/ios/WatermelonDB/DatabasePlatformIOS.mm b/native/ios/WatermelonDB/DatabasePlatformIOS.mm index 609889bfc..16e5cdf54 100644 --- a/native/ios/WatermelonDB/DatabasePlatformIOS.mm +++ b/native/ios/WatermelonDB/DatabasePlatformIOS.mm @@ -19,6 +19,39 @@ void initializeSqlite() { } std::string resolveDatabasePath(std::string path) { +#if TARGET_OS_MACCATALYST + // On Mac Catalyst (unsandboxed), NSDocumentDirectory resolves to + // ~/Documents and any access there triggers the macOS TCC prompt + // asking the user to grant access to their Documents folder at + // launch. Use Application Support instead — per-app, private to the + // bundle, and never triggers TCC. + // + // We intentionally don't migrate from the legacy ~/Documents path: + // probing for a legacy file would itself trigger the TCC prompt + // we're trying to avoid. + NSError *err = nil; + NSURL *appSupport = [NSFileManager.defaultManager URLForDirectory:NSApplicationSupportDirectory + inDomain:NSUserDomainMask + appropriateForURL:nil + create:YES + error:&err]; + + if (err) { + NSLog(@"Error: %@", err); + throw std::runtime_error("Failed to resolve database path - could not find Application Support URL"); + } + + NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier] ?: @"WatermelonDB"; + NSURL *dir = [appSupport URLByAppendingPathComponent:bundleId isDirectory:YES]; + [NSFileManager.defaultManager createDirectoryAtURL:dir + withIntermediateDirectories:YES + attributes:nil + error:nil]; + NSString *dbPath = [dir URLByAppendingPathComponent: + [NSString stringWithFormat:@"%s.db", path.c_str()]].path; + + return std::string([dbPath cStringUsingEncoding:NSUTF8StringEncoding]); +#else // Default: app documents/.db NSError *err = nil; NSURL *documentsUrl = [NSFileManager.defaultManager URLForDirectory:NSDocumentDirectory @@ -36,6 +69,7 @@ void initializeSqlite() { [NSString stringWithFormat:@"%s.db", path.c_str()]].path; return std::string([dbPath cStringUsingEncoding:NSUTF8StringEncoding]); +#endif } void deleteDatabaseFile(std::string path, bool warnIfDoesNotExist) { diff --git a/native/ios/WatermelonDB/objc/WMDatabaseDriver.m b/native/ios/WatermelonDB/objc/WMDatabaseDriver.m index 020e0832b..b751957f0 100644 --- a/native/ios/WatermelonDB/objc/WMDatabaseDriver.m +++ b/native/ios/WatermelonDB/objc/WMDatabaseDriver.m @@ -25,6 +25,29 @@ - (NSString *) pathForName:(NSString *)dbName if ([dbName hasPrefix:@"file:"] || [dbName containsString:@"/"]) { return dbName; } else { +#if TARGET_OS_MACCATALYST + // On Mac Catalyst (unsandboxed), NSDocumentDirectory resolves to + // ~/Documents and any access there triggers the macOS TCC prompt + // asking the user to grant access to their Documents folder at + // launch. Use Application Support instead — per-app, private to + // the bundle, and never triggers TCC. + // + // We intentionally don't migrate from the legacy ~/Documents path: + // probing for a legacy file would itself trigger the TCC prompt + // we're trying to avoid. + NSURL *appSupport = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory + inDomain:NSUserDomainMask + appropriateForURL:nil + create:YES + error:nil]; + NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier] ?: @"WatermelonDB"; + NSURL *dir = [appSupport URLByAppendingPathComponent:bundleId isDirectory:YES]; + [[NSFileManager defaultManager] createDirectoryAtURL:dir + withIntermediateDirectories:YES + attributes:nil + error:nil]; + return [[dir URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.db", dbName]] path]; +#else NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil @@ -32,6 +55,7 @@ - (NSString *) pathForName:(NSString *)dbName error:nil]; return [[url URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.db", dbName]] path]; +#endif } }