diff --git a/ios/InternxtFileProvider/FileProviderEnumerator.swift b/ios/InternxtFileProvider/FileProviderEnumerator.swift new file mode 100644 index 000000000..2bbf8263b --- /dev/null +++ b/ios/InternxtFileProvider/FileProviderEnumerator.swift @@ -0,0 +1,57 @@ +// +// FileProviderEnumerator.swift +// InternxtFileProvider +// +// Created by Ramon Candel on 9/4/26. +// + +import FileProvider + +class FileProviderEnumerator: NSObject, NSFileProviderEnumerator { + + private let enumeratedItemIdentifier: NSFileProviderItemIdentifier + private let anchor = NSFileProviderSyncAnchor("an anchor".data(using: .utf8)!) + + init(enumeratedItemIdentifier: NSFileProviderItemIdentifier) { + self.enumeratedItemIdentifier = enumeratedItemIdentifier + super.init() + } + + func invalidate() { + // TODO: perform invalidation of server connection if necessary + } + + func enumerateItems(for observer: NSFileProviderEnumerationObserver, startingAt page: NSFileProviderPage) { + /* TODO: + - inspect the page to determine whether this is an initial or a follow-up request + + If this is an enumerator for a directory, the root container or all directories: + - perform a server request to fetch directory contents + If this is an enumerator for the active set: + - perform a server request to update your local database + - fetch the active set from your local database + + - inform the observer about the items returned by the server (possibly multiple times) + - inform the observer that you are finished with this page + */ + observer.didEnumerate([FileProviderItem(identifier: NSFileProviderItemIdentifier("a file"))]) + observer.finishEnumerating(upTo: nil) + } + + func enumerateChanges(for observer: NSFileProviderChangeObserver, from anchor: NSFileProviderSyncAnchor) { + /* TODO: + - query the server for updates since the passed-in sync anchor + + If this is an enumerator for the active set: + - note the changes in your local database + + - inform the observer about item deletions and updates (modifications + insertions) + - inform the observer when you have finished enumerating up to a subsequent sync anchor + */ + observer.finishEnumeratingChanges(upTo: anchor, moreComing: false) + } + + func currentSyncAnchor(completionHandler: @escaping (NSFileProviderSyncAnchor?) -> Void) { + completionHandler(anchor) + } +} diff --git a/ios/InternxtFileProvider/FileProviderExtension.swift b/ios/InternxtFileProvider/FileProviderExtension.swift new file mode 100644 index 000000000..8002e1259 --- /dev/null +++ b/ios/InternxtFileProvider/FileProviderExtension.swift @@ -0,0 +1,61 @@ +// +// FileProviderExtension.swift +// InternxtFileProvider +// +// Created by Ramon Candel on 9/4/26. +// + +import FileProvider +import InternxtSwiftCore + +class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension { + required init(domain: NSFileProviderDomain) { + // TODO: The containing application must create a domain using `NSFileProviderManager.add(_:, completionHandler:)`. The system will then launch the application extension process, call `FileProviderExtension.init(domain:)` to instantiate the extension for that domain, and call methods on the instance. + super.init() + } + + func invalidate() { + // TODO: cleanup any resources + } + + func item(for identifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) -> Progress { + // resolve the given identifier to a record in the model + + // TODO: implement the actual lookup + + completionHandler(FileProviderItem(identifier: identifier), nil) + return Progress() + } + + func fetchContents(for itemIdentifier: NSFileProviderItemIdentifier, version requestedVersion: NSFileProviderItemVersion?, request: NSFileProviderRequest, completionHandler: @escaping (URL?, NSFileProviderItem?, Error?) -> Void) -> Progress { + // TODO: implement fetching of the contents for the itemIdentifier at the specified version + + completionHandler(nil, nil, NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:])) + return Progress() + } + + func createItem(basedOn itemTemplate: NSFileProviderItem, fields: NSFileProviderItemFields, contents url: URL?, options: NSFileProviderCreateItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?) -> Void) -> Progress { + // TODO: a new item was created on disk, process the item's creation + + completionHandler(itemTemplate, [], false, nil) + return Progress() + } + + func modifyItem(_ item: NSFileProviderItem, baseVersion version: NSFileProviderItemVersion, changedFields: NSFileProviderItemFields, contents newContents: URL?, options: NSFileProviderModifyItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?) -> Void) -> Progress { + // TODO: an item was modified on disk, process the item's modification + + completionHandler(nil, [], false, NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:])) + return Progress() + } + + func deleteItem(identifier: NSFileProviderItemIdentifier, baseVersion version: NSFileProviderItemVersion, options: NSFileProviderDeleteItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (Error?) -> Void) -> Progress { + // TODO: an item was deleted on disk, process the item's deletion + + completionHandler(NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:])) + return Progress() + } + + func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier, request: NSFileProviderRequest) throws -> NSFileProviderEnumerator { + return FileProviderEnumerator(enumeratedItemIdentifier: containerItemIdentifier) + } +} diff --git a/ios/InternxtFileProvider/FileProviderItem.swift b/ios/InternxtFileProvider/FileProviderItem.swift new file mode 100644 index 000000000..12ad83dc6 --- /dev/null +++ b/ios/InternxtFileProvider/FileProviderItem.swift @@ -0,0 +1,45 @@ +// +// FileProviderItem.swift +// InternxtFileProvider +// +// Created by Ramon Candel on 9/4/26. +// + +import FileProvider +import UniformTypeIdentifiers + +class FileProviderItem: NSObject, NSFileProviderItem { + + // TODO: implement an initializer to create an item from your extension's backing model + // TODO: implement the accessors to return the values from your extension's backing model + + private let identifier: NSFileProviderItemIdentifier + + init(identifier: NSFileProviderItemIdentifier) { + self.identifier = identifier + } + + var itemIdentifier: NSFileProviderItemIdentifier { + return identifier + } + + var parentItemIdentifier: NSFileProviderItemIdentifier { + return .rootContainer + } + + var capabilities: NSFileProviderItemCapabilities { + return [.allowsReading, .allowsWriting, .allowsRenaming, .allowsReparenting, .allowsTrashing, .allowsDeleting] + } + + var itemVersion: NSFileProviderItemVersion { + NSFileProviderItemVersion(contentVersion: "a content version".data(using: .utf8)!, metadataVersion: "a metadata version".data(using: .utf8)!) + } + + var filename: String { + return identifier.rawValue + } + + var contentType: UTType { + return identifier == NSFileProviderItemIdentifier.rootContainer ? .folder : .plainText + } +} diff --git a/ios/InternxtFileProvider/Info.plist b/ios/InternxtFileProvider/Info.plist new file mode 100644 index 000000000..5236f8de8 --- /dev/null +++ b/ios/InternxtFileProvider/Info.plist @@ -0,0 +1,19 @@ + + + + + NSExtension + + NSExtensionFileProviderDocumentGroup + group.com.internxt.snacks + NSExtensionFileProviderSupportsEnumeration + + NSExtensionPointIdentifier + com.apple.fileprovider-nonui + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).FileProviderExtension + + RCTNewArchEnabled + + + diff --git a/ios/InternxtFileProvider/InternxtFileProvider.entitlements b/ios/InternxtFileProvider/InternxtFileProvider.entitlements new file mode 100644 index 000000000..2b5a4e963 --- /dev/null +++ b/ios/InternxtFileProvider/InternxtFileProvider.entitlements @@ -0,0 +1,15 @@ + + + + + com.apple.security.application-groups + + group.com.internxt.snacks + + keychain-access-groups + + $(AppIdentifierPrefix)group.com.internxt.snacks + + + + diff --git a/ios/InternxtFileProvider/InternxtFileProvider.plist b/ios/InternxtFileProvider/InternxtFileProvider.plist new file mode 100644 index 000000000..7a62a22e9 --- /dev/null +++ b/ios/InternxtFileProvider/InternxtFileProvider.plist @@ -0,0 +1,12 @@ + + + + + +