Hi everyone,
I am a beginner in iOS/Swift programming. I'm trying to develop a mobile application that allows to mount a network drive in the iphone Files application via the WebDav protocol.
I saw on the internet that WebDav is no longer implemented in iOS because considered deprecated by apple.
To accomplish this task, I decided to separate responsibilities as follows:
Framework:
- WebDav (responsible for communication with the WebDav server)
FileProviderExtension:
- FileBridge (Responsible for bridging the gap between the WebDav Framework and the iOS Files app)
Main App
I also have an AppGroup that includes the main application and the fileproviderextension
Initially, to measure the feasibility and complexity of this task, I'd like to make a simplistic version that simply displays the files on my drive in the Files app, without necessarily being able to interact with them.
FileProviderExtension.swift:
import FileProvider
import WebDav
class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension {
private var webDavService: WebDavService?
required init(domain: NSFileProviderDomain)
{
super.init()
self.webDavService = WebDavService(baseURL: URL(string: "https://www.mydrive.com/drive")!)
}
func invalidate() {
// TODO: cleanup any resources
}
func item(for identifier: NSFileProviderItemIdentifier,
request: NSFileProviderRequest,
completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) -> Progress {
let progress = Progress(totalUnitCount: 1)
Task {
do {
if let items = try await webDavService?.propfind(path: identifier.rawValue, depth: 1),
let item = items.first(where: { $0.itemIdentifier == identifier }) {
completionHandler(item, nil)
} else {
completionHandler(nil, NSError(domain: NSCocoaErrorDomain,
code: NSFileNoSuchFileError,
userInfo: nil))
}
} catch {
completionHandler(nil, error)
}
}
return progress
}
func fetchContents(for itemIdentifier: NSFileProviderItemIdentifier,
version requestedVersion: NSFileProviderItemVersion?,
request: NSFileProviderRequest,
completionHandler: @escaping (URL?, NSFileProviderItem?, Error?) -> Void) -> Progress {
let progress = Progress(totalUnitCount: 1)
Task {
do {
guard let service = webDavService else {
throw WebDavError.invalidResponse
}
let data = try await service.get(fileAt: itemIdentifier.rawValue)
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(itemIdentifier.rawValue)
try data.write(to: tempURL)
completionHandler(tempURL, nil, nil)
} catch {
completionHandler(nil, nil, error)
}
}
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,
service: webDavService)
}
}
Here's the code I use to initialize my domain in the main app files:
fileprivate func registerFileProviderDomain() {
let domainIdentifier = NSFileProviderDomainIdentifier("FileProviderExtension Bundle Identifier")
let domain = NSFileProviderDomain(identifier: domainIdentifier,
displayName: "My Drive")
NSFileProviderManager.add(domain) { error in
NSFileProviderManager.add(domain) { error in
if let error = error {
print("Error cannot add domain file provider : \(error.localizedDescription)")
} else {
print("Success domain file provider added")
}
}
}
I can't get rid of the Error : Error cannot add domain file provider : The operation couldn’t be completed. Invalid argument.
I don't know what I'm missing Please help me understand