I'm currently working on the iOS 11 File Provider extension to enumerate files stored remotely by our service. So far I've been able to browse through my cached database of Files, as well as an cloud enumerations of my item.
My next step is to create the working set, but so far I haven't found any success having the system enumerate with my working set enumerator. The enumerator is created, but never enumerated, whether it's enumerating items or changes.
I work with a local database for my items, keep track of the lastUsedDate
, tagData
and the favoriteRank
. They are not yet sync across devices yet but that should not prevent the local device to display these items.
Every time my host app is displaying a file, it sets its lastUsedDate
and call signalEnumerator(for: myItemIdentifier)
and signalEnumerator(for: .workingSet)
.
My NSFileProviderExtension
subclass implements setTagData(...)
, setFavoriteRank(...)
and setLastUsedDate(...)
as well.
I have a dedicated class to enumerate through the working set as follow:
class WorkingSetEnumerator: NSObject, NSFileProviderEnumerator {
let items: [NSFileProviderItem]
// Initialise with the workings set items, might not be correct but should work as first step
init(items: [NSFileProviderItem]) {
self.items = items
super.init()
}
func invalidate() {
// Empty since it is a "dummy" implementation
}
func enumerateItems(for observer: NSFileProviderEnumerationObserver, startingPage page: NSFileProviderPage) {
observer.didEnumerate(self.items)
observer.finishEnumerating(upTo: nil)
}
func enumerateChanges(for observer: NSFileProviderChangesObserver, from anchor: NSFileProviderSyncAnchor) {
//TODO: Implement!
fatalError("Not Implemented, yet!")
}
}
Nothings really fancy here. It is initialized with dummy data here for the sake of example, but I also tried with live data and same issue.
The function requesting an enumerator is implemented as follow: override func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier) throws -> NSFileProviderEnumerator {
if containerItemIdentifier == .rootDirectory {
// return a folder enumerator on the root
} else if containerItemIdentifier == .workingSet {
let items = [DummyItem()]
return WorkingSetEnumerator(items: items)
} else {
// return Folder or File enumerator based on the item
}
}
Here is the implemtation of my DummyItem:
class DummyFileItem: NSObject, NSFileProviderItem {
var parentItemIdentifier: NSFileProviderItemIdentifier {
return NSFileProviderItemIdentifier.rootContainer
}
var itemIdentifier: NSFileProviderItemIdentifier {
return NSFileProviderItemIdentifier("Dummy")
}
var filename: String {
return "Dummy"
}
var typeIdentifier: String {
return "public.folder"
}
var isShared: Bool {
return true
}
var isTrashed: Bool {
return false
}
var lastUsedDate: Date? {
return Date()
}
var favoriteRank: NSNumber? {
return NSNumber(value: NSFileProviderFavoriteRankUnranked)
}
var tagData: Data? {
return nil
}
var documentSize: NSNumber? {
return NSNumber(value: 0)
}
var childItemCount: NSNumber? {
return NSNumber(value: 0)
}
var isDownloading: Bool {
return false
}
var isDownloaded: Bool {
return true
}
var downloadingError: Error? {
return nil
}
var isUploaded: Bool {
return true
}
var isUploading: Bool {
return false
}
var uploadingError: Error? {
return nil
}
var isMostRecentVersionDownloaded: Bool {
return true
}
var isSharedByCurrentUser: Bool {
return true
}
}
Simply some dummy data that should display my item at least in the recent tab.
As described earlier, I also tried that with my local items, that contains real data, and the same happens, the enumerator is created but not enumerated.
Is there any way to force the enumeration of the working set? Any idea base on my description of what could be missing?
Thank you for your time and help.
I managed to make it work, thanks to this answer on Apple forum: https://forums.developer.apple.com/thread/100172?q=iOS11%20File%20Provider%20working%20set.
What was missing was a
NSFileProviderSyncAnchor
for the working set. For my dummy data, the sync anchor provides only anInt64
hardcoded to 0, and the working set is now enumerated once, which is enough for my dummy example.Now to make it work on my actual data, I will need to correctly keep track of it everytime I make changes to files belonging to the working set, and signal changes to the working set.'
Hope this will help someone, as this isn't trivial nor very well documented.