Skip to content

Commit 628d78e

Browse files
committed
feat: Dedicated directories for file provider domain support files.
Signed-off-by: Iva Horn <iva.horn@icloud.com>
1 parent 3e00b22 commit 628d78e

3 files changed

Lines changed: 60 additions & 32 deletions

File tree

Sources/NextcloudFileProviderKit/Database/FilesDatabaseManager.swift

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ public final class FilesDatabaseManager: Sendable {
6666
///
6767
/// - Returns: The location of the database files directory.
6868
///
69-
private static func assertDatabaseDirectory() -> URL {
69+
private static func assertDatabaseDirectory(for identifier: NSFileProviderDomainIdentifier) -> URL {
7070
Self.logger.debug("Asserting existence of database directory...")
7171

7272
let manager = FileManager.default
7373

74-
guard let fileProviderExtensionDataDirectory = urlForFileProviderExtensionData() else {
74+
guard let fileProviderExtensionDataDirectory = manager.fileProviderDomainSupportDirectory(for: identifier) else {
7575
Self.logger.fault("Failed to resolve the file provider extension data directory!")
7676
assertionFailure("Failed to resolve the file provider extension data directory!")
7777
return manager.temporaryDirectory // Only to satisfy the non-optional return type. The extension is unusable at this point anyway.
@@ -108,13 +108,20 @@ public final class FilesDatabaseManager: Sendable {
108108
/// - account: The Nextcloud account for which the database is being created.
109109
/// - customDatabaseDirectory: Optional custom directory where the database files should be stored. If not provided, the default directory will be used.
110110
///
111-
public init(realmConfiguration customConfiguration: Realm.Configuration? = nil, account: Account, databaseDirectory customDatabaseDirectory: URL? = nil) {
111+
public init(realmConfiguration customConfiguration: Realm.Configuration? = nil, account: Account, databaseDirectory customDatabaseDirectory: URL? = nil, fileProviderDomainIdentifier: NSFileProviderDomainIdentifier) {
112112
self.account = account
113113

114114
Self.logger.info("Initializing for account: \(account.ncKitAccount, privacy: .public)")
115115

116-
let databaseDirectory = customDatabaseDirectory ?? Self.assertDatabaseDirectory()
117-
let accountDatabaseFilename = account.fileName + "-" + Self.databaseFilename
116+
let databaseDirectory = customDatabaseDirectory ?? Self.assertDatabaseDirectory(for: fileProviderDomainIdentifier)
117+
let accountDatabaseFilename: String
118+
119+
if UUID(uuidString: fileProviderDomainIdentifier.rawValue) != nil {
120+
accountDatabaseFilename = "\(fileProviderDomainIdentifier.rawValue).realm"
121+
} else {
122+
accountDatabaseFilename = account.fileName + "-" + Self.databaseFilename
123+
}
124+
118125
let databaseLocation = databaseDirectory.appendingPathComponent(accountDatabaseFilename)
119126

120127
let configuration = customConfiguration ?? Realm.Configuration(
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import FileProvider
2+
import Foundation
3+
4+
public extension FileManager {
5+
///
6+
/// Return the sandboxed application support directory specific to the file provider domain distinguished by the given identifier.
7+
///
8+
/// If such directory does not exist yet, this attempts to create it implicitly.
9+
///
10+
/// > Legacy Support: In the past, a subdirectory in the application group container was used for everything.
11+
/// This caused crashes due to violations of sandbox restrictions.
12+
/// If already existent, the legacy location will be used.
13+
/// Otherwise the data will be stored in a new location.
14+
///
15+
/// - Parameters:
16+
/// - identifier: File provider domain identifier which is used to isolate application support data for different file provider domains of the same extension.
17+
///
18+
/// - Returns: A directory based on what the system returns for looking up standard directories. Likely in the sandbox containers of the file provider extension. Very unlikely to fail by returning `nil`.
19+
///
20+
func fileProviderDomainSupportDirectory(for identifier: NSFileProviderDomainIdentifier) -> URL? {
21+
// Legacy directory support.
22+
if let containerUrl = pathForAppGroupContainer() {
23+
let legacyLocation = containerUrl.appendingPathComponent("FileProviderExt")
24+
25+
if FileManager.default.fileExists(atPath: legacyLocation.path) {
26+
return legacyLocation
27+
}
28+
}
29+
30+
// Designated file provider domain directories.
31+
guard let applicationSupportDirectory = try? FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else {
32+
return nil
33+
}
34+
35+
let domainsSupportDirectory = applicationSupportDirectory.appendingPathComponent("File Provider Domains")
36+
let fileProviderDomainSupportDirectory = domainsSupportDirectory.appendingPathComponent(identifier.rawValue)
37+
38+
if fileExists(atPath: fileProviderDomainSupportDirectory.path) == false {
39+
do {
40+
try createDirectory(at: fileProviderDomainSupportDirectory, withIntermediateDirectories: true)
41+
} catch {
42+
return nil
43+
}
44+
}
45+
46+
return fileProviderDomainSupportDirectory
47+
}
48+
}

Sources/NextcloudFileProviderKit/Utilities/LocalFiles.swift

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,33 +32,6 @@ public func pathForAppGroupContainer() -> URL? {
3232
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier)
3333
}
3434

35-
///
36-
/// Resolve the path where the file provider extension store its data.
37-
///
38-
/// In the past, this used a subdirectory in the application group container.
39-
/// If already existent, this will still be used.
40-
/// Otherwise the data will be stored in a new location.
41-
/// To comply with sandboxing and conventional directory structure of the platform, this uses the dedicated application support directory in the container of the file provider extension instead.
42-
///
43-
/// - Returns: The root location in which the extension can store its specific data.
44-
///
45-
public func urlForFileProviderExtensionData() -> URL? {
46-
if let containerUrl = pathForAppGroupContainer() {
47-
let legacyLocation = containerUrl.appendingPathComponent("FileProviderExt")
48-
49-
if FileManager.default.fileExists(atPath: legacyLocation.path) {
50-
return legacyLocation
51-
}
52-
}
53-
54-
do {
55-
return try FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
56-
} catch {
57-
lfuLogger.error("Failed to get URL for application support directory in user mask!")
58-
return nil
59-
}
60-
}
61-
6235
public func pathForFileProviderTempFilesForDomain(_ domain: NSFileProviderDomain) throws -> URL? {
6336
guard let fpManager = NSFileProviderManager(for: domain) else {
6437
lfuLogger.error("Unable to get file provider manager for domain: \(domain.displayName, privacy: .public)")

0 commit comments

Comments
 (0)