Items in File Provider Extension for iOS 11

Items in File provider extension for ios 11

Finally I figured out the problem here, that if we want to show the files in the root page the NSFileProviderItem requires it's parent_id as NSFileProviderItemIdentifier.rootContainer. Setting this solved my problem

iOS 11 FileProvider NSFileProviderRootContainerItemIdentifier

Return a new NSFileProviderItem For NSFileProviderRootContainerItemIdentifier.

don't return nil,that will cause a problem when try to use Filprovider extension in other app.

File Provider iOS11 startProvidingItem not invoked

Turns out, I did not correctly implement providePlaceholder(at url:).

It is now solved.

Cheers

-nls

EDIT:

In order to list the items in your file provider, the method enumerator(for:) should be implemented.
This method will receive a containerItemIdentifier, as if telling you "what folder the user is trying to access". It returns a NSFileProviderEnumerator object, that should also be implemented by you.

Here is an example of how a simple enumerator(for:) method should look like:

class FileProviderExtension: NSFileProviderExtension {

override func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier) throws -> NSFileProviderEnumerator {

var enumerator: NSFileProviderEnumerator? = nil

if (containerItemIdentifier == NSFileProviderItemIdentifier.rootContainer) {
enumerator = FileProviderEnumerator(enumeratedItemIdentifier: containerItemIdentifier)
}
else {
enumerator = FileProviderEnumerator(enumeratedItemIdentifier: containerItemIdentifier)
}
if enumerator == nill {
throw NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:])
}
return enumerator
}

(...)

}

Again, as I said, the FileProviderEnumerator should be implemented by you. The important method here is the enumerateItems(for observer:, startingAt page:)

Here it is how it should look:

class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {

func enumerateItems(for observer: NSFileProviderEnumerationObserver, startingAt page: NSFileProviderPage) {

if (enumeratedItemIdentifier == NSFileProviderItemIdentifier.rootContainer) {

//Creating an example of a folder item
let folderItem = FileProviderFolder()
folderItem.parentItemIdentifier = enumeratedItemIdentifier //<-- Very important
folderItem.typeIdentifier = "public.folder"
folderItem.name = "ExampleFolder"
folderItem.id = "ExampleFolderID"

//Creating an example of a file item
let fileItem = FileProviderFile()
fileItem.parentItemIdentifier = enumeratedItemIdentifier //<-- Very important
fileItem.typeIdentifier = "public.plain-text"
fileItem.name = "ExampleFile.txt"
fileItem.id = "ExampleFileID"

self.itemList.append(contentsOf: [folderItem, fileItem])
observer.didEnumerate(self.itemList)
observer.finishEnumerating(upTo: nil)

}
else {
//1 > Find directory name using "enumeratedItemIdentifier" property
//2 > Fetch data from the desired directory
//3 > Create File or Folder Items
//4 > Send items back using didEnumerate and finishEnumerating
}
}

(...)

}

Remember that we were creating these FileProviderEnumerators, giving them the containerItemIdentifier. This property is used to determine what folder the user is trying to access.

Very important note: Each item, File or Folder, should have its parentItemIdentifier property defined. If this property is not set, the items won't appear when the user tries to open the parent folder.
Also, as the name suggests, typeIdentifier will hold the Uniform Type Identifier (UTI) for the item.

Finally, the last object we should implement is the NSFileProviderItem. Both File and Folder items are very similar, and should differ in their typeIdentifier property.
Here is a very simple example of a folder:

class FileProviderFolder: NSObject, NSFileProviderItem {

public var id: String?
public var name: String?

var parentItemIdentifier: NSFileProviderItemIdentifier
var typeIdentifier: String

init() {

}

var itemIdentifier: NSFileProviderItemIdentifier {
return NSFileProviderItemIdentifier(self.id!)
}

var filename: String {
return self.name!
}
}

The itemIdentifier is very important because, as stated before, this property will provide the directory name for the folder item when trying to enumerate its contents (refer to enumerator(for:) method).

EDIT2

If the user selects a file, the method startProvidingItem(at url:) should be called.
This method should perform 3 tasks:

1 - Find the selected item ID (usualy using the provided url, but you can use a database too)

2 - Download the file to the local device, making it available at the specified url. Alamofire does this;

3 - Call completionHandler;

Here is a simple example of this method:

class FileProviderExtension: NSFileProviderExtension {

override func urlForItem(withPersistentIdentifier identifier: NSFileProviderItemIdentifier) -> URL? {
// resolve the given identifier to a file on disk
guard let item = try? item(for: identifier) else {
return nil
}
// in this implementation, all paths are structured as <base storage directory>/<item identifier>/<item file name>
let perItemDirectory = NSFileProviderManager.default.documentStorageURL.appendingPathComponent(identifier.rawValue, isDirectory: true)
let allDir = perItemDirectory.appendingPathComponent(item.filename, isDirectory:false)
return allDir
}

override func persistentIdentifierForItem(at url: URL) -> NSFileProviderItemIdentifier? {
// exploit that the path structure has been defined as <base storage directory>/<item identifier>/<item file name>, at urlForItem
let pathComponents = url.pathComponents
assert(pathComponents.count > 2)
return NSFileProviderItemIdentifier(pathComponents[pathComponents.count - 2])
}

override func startProvidingItem(at url: URL, completionHandler: @escaping (Error?) -> Void) {

guard
let itemID = persistentIdentifierForItem(at: url),
let item = try? self.item(for: itemID) as! FileProviderFile else {
return
}

DownloadfileAsync(
file: item,
toLocalDirectory: url,
success: { (response) in

// Do necessary processing on the FileProviderFile object
// Example: setting isOffline flag to True

completionHandler(nil)
},
fail: { (response) in
completionHandler(NSFileProviderError(.serverUnreachable))
}
)

}

(...)
}

Note that, to get the ID from the URL, I'm using the recomended method: the URL it self contains the item ID.

This URL is definedin the urlForItem method.

Hope this helps.

-nls

File provider extension upload - iOS11

It turned out to be memory issue. When my file provider reaches ~15MB in memory, app crashes. This caused due to file upload chunk size. I have reduced chunk size and works fine. My guess is, by the time ARC frees up uploaded chunks, memory accumulated to 15MB in my use case.

iOS 11 Files App is not persisting Tags metadata stored for Files in working set

Each File provider is expected to maintain its own Database for all the offline files (Which are Recents, Tags etc.). When ever files App is launched it calls provideURL() method which will be overridden by File providers. Once we get this call we have to query our DB for offline items and provide URLs for the same.

Also Working set Sync Anchor should be maintained correctly otherwise we wont get call backs to add items to Files App working set.



Related Topics



Leave a reply



Submit