热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

文件提供程序iOS11startProvidingItem未被调用-FileProvideriOS11startProvidingItemnotinvoked

ImimplementingaFileProviderExtensionforiOS11.我正在为iOS11实现文件提供程序扩展。Dispitewatchingthec

I'm implementing a File Provider Extension for iOS 11.

我正在为iOS 11实现文件提供程序扩展。

Dispite watching the conference at https://developer.apple.com/videos/play/wwdc2017/243/ and navigating through Apple's Documentation, I still can't seem to understand how to implement some of the methods for NSFileProviderExtension and NSFileProviderEnumerator objects.

在https://developer.apple.com/videos/play/wwdc2017/243/上观看会议并浏览Apple的文档时,我仍然无法理解如何实现NSFileProviderExtension和NSFileProviderEnumerator对象的某些方法。

I successfully implemented NSFileProviderItem, having all of them listed in the Navite iOS 11 Files App. However, I can't trigger any document based app to open upon selecting a file.

我成功实现了NSFileProviderItem,其中所有这些都列在了Navite iOS 11文件应用程序中。但是,我无法触发任何基于文档的应用程序在选择文件时打开。

I overrided all the methods for the NSFileProviderExtension. Some are still empty, but I placed a breakpoint to check whenever they are called.

我重写了NSFileProviderExtension的所有方法。有些仍然是空的,但是我设置了断点来检查它们何时被调用。

The NSFileProviderExtension looks something like this:

NSFileProviderExtension看起来像这样:

class FileProviderExtension: NSFileProviderExtension {
    var db : [FileProviderItem]  = [] //Used "as" a database
...

    override func item(for identifier: NSFileProviderItemIdentifier) throws -> NSFileProviderItem {
        for i in db {
            if i.itemIdentifier.rawValue == identifier.rawValue {
                return i
            }
        }
        throw NSError(domain: NSCocoaErrorDomain, code: NSNotFound, userInfo:[:])
    }

    override func urlForItem(withPersistentIdentifier identifier: NSFileProviderItemIdentifier) -> URL? {
        guard let item = try? item(for: identifier) else {
            return nil
        }

        // in this implementation, all paths are structured as //
        let manager = NSFileProviderManager.default
        let perItemDirectory = manager.documentStorageURL.appendingPathComponent(identifier.rawValue, isDirectory: true)

        return perItemDirectory.appendingPathComponent(item.filename, isDirectory:false)
    }

    // MARK: - Enumeration
    func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier) throws -> NSFileProviderEnumerator {
        var maybeEnumerator: NSFileProviderEnumerator? = nil

        if (cOntainerItemIdentifier== NSFileProviderItemIdentifier.rootContainer) {
            maybeEnumerator = FileProviderEnumerator(enumeratedItemIdentifier: containerItemIdentifier)
            self.db = CustomData.getData(pid: containerItemIdentifier)

        } else if (cOntainerItemIdentifier== NSFileProviderItemIdentifier.workingSet) {
            // TODO: instantiate an enumerator for the working set
        } else {

        }
        guard let enumerator = maybeEnumerator else {
            throw NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:])
        }
        return enumerator
    }

My enumerateItems looks something like so:

我的enumerateItems看起来像这样:

class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {

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

        let itens = CustomData.getData(pid: enumeratedItemIdentifier)
        observer.didEnumerate(itens)
        observer.finishEnumerating(upTo: nil)

    }

The static function CustomData.getData is used for testing. It returns an array of NSFileProviderItem with the desired properties. It should be replaced with a database, as explained in the conference.

静态函数CustomData.getData用于测试。它返回一个具有所需属性的NSFileProviderItem数组。如会议中所述,应将其替换为数据库。

class CustomData {


    static func getData(pid : NSFileProviderItemIdentifier) -> [FileProviderItem] {
        return [
            FileProviderItem(uid: "0", pid: pid, name: "garden", remoteUrl : "https://img2.10bestmedia.com/Images/Photos/338373/GettyImages-516844708_54_990x660.jpg"),
            FileProviderItem(uid: "1", pid: pid, name: "car", remoteUrl : "https://static.pexels.com/photos/170811/pexels-photo-170811.jpeg"),
            FileProviderItem(uid: "2", pid: pid, name: "cat", remoteUrl : "http://www.petmd.com/sites/default/files/what-does-it-mean-when-cat-wags-tail.jpg"),
            FileProviderItem(uid: "3", pid: pid, name: "computer", remoteUrl : "http://mrslamarche.com/wp-content/uploads/2016/08/dell-xps-laptop-620.jpg")
        ]
    }

}

The problem is, when the user presses a document, urlForItem is successfully called but nothing happens upon returning the item url.

问题是,当用户按下文档时,urlForItem被成功调用,但在返回项目URL时没有任何反应。

What am I doing wrong? I can't find any examples on the internet.

我究竟做错了什么?我在互联网上找不到任何例子。

Cheers

-nls

2 个解决方案

#1


6  

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

事实证明,我没有正确实现providePlaceholder(在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.

为了列出文件提供程序中的项目,应该实现方法枚举器(for :)。此方法将接收containerItemIdentifier,就像告诉您“用户尝试访问的文件夹”一样。它返回一个NSFileProviderEnumerator对象,该对象也应由您实现。

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

下面是一个简单的枚举器(for :)方法应如何显示的示例:

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:)

同样,正如我所说,FileProviderEnumerator应该由您实现。这里重要的方法是enumerateItems(对于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.

请记住,我们正在创建这些FileProviderEnumerator,为它们提供containerItemIdentifier。此属性用于确定用户尝试访问的文件夹。

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.

非常重要的注意事项:每个项目(文件或文件夹)都应定义其parentItemIdentifier属性。如果未设置此属性,则当用户尝试打开父文件夹时,不会显示这些项。此外,顾名思义,typeIdentifier将保存项目的统一类型标识符(UTI)。

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:

最后,我们应该实现的最后一个对象是NSFileProviderItem。文件和文件夹项都非常相似,并且它们的typeIdentifier属性应该不同。这是一个非常简单的文件夹示例:

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).

itemIdentifier非常重要,因为如前所述,此属性将在尝试枚举其内容时提供文件夹项的目录名(请参阅枚举器(for :)方法)。

EDIT2

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

如果用户选择文件,则应调用方法startProvidingItem(在url :)。此方法应执行3个任务:

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

1 - 查找所选的项目ID(通常使用提供的URL,但您也可以使用数据库)

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

2 - 将文件下载到本地设备,使其在指定的URL上可用。 Alamofire这样做;

3 - Call completionHandler;

3 - 调用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 //
        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 //, 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.

请注意,要从URL获取ID,我使用推荐的方法:它自己包含项目ID的URL。

This URL is definedin the urlForItem method.

此URL在urlForItem方法中定义。

Hope this helps.

希望这可以帮助。

-nls

#2


1  

I thought I'd provide a followup answer, the primary answer is great as a first step. In my case startProvidingItem was not called because I was not storing the files in exactly the directory the system was looking for, that is to say:

我以为我会提供一个后续答案,作为第一步,主要答案是很好的。在我的情况下,没有调用startProvidingItem,因为我没有将文件存储在系统正在寻找的目录中,也就是说:

/File Provider Storage//My Awesome Image.png

That is on the slide from WWDC17 on the FileProvider extension, but I did not think it must follow that format so exactly.

这是在WWDC17的FileProvider扩展上的幻灯片上,但我认为它不一定必须遵循这种格式。

I had a directory not named "File Provider Storage" into which I was putting files directly, and startProvidingItem was never called. It was only when I made a directory for the uniqueFileID into which the file was placed, AND renamed my overall storage directory to "File Provider Storage" that startProvidingItem was called.

我有一个名为“文件提供程序存储”的目录,我直接放入文件,并且从未调用startProvidingItem。只有当我为放置文件的uniqueFileID创建目录时,才将我的整个存储目录重命名为“File Provider Storage”,调用startProvidingItem。

Also note that with iOS11, you'll need to provide a providePlaceholder call as well to the FileProviderExtension, use EXACTLY the code that is in the docs for that and do not deviate unless you are sure of what you are doing.

另请注意,对于iOS11,您还需要向FileProviderExtension提供一个providePlaceholder调用,请使用文档中的代码,并且不要偏离,除非您确定自己在做什么。


推荐阅读
  • iOS 开发技巧:TabBarController 自定义与本地通知设置
    本文介绍了如何在 iOS 中自定义 TabBarController 的背景颜色和选中项的颜色,以及如何使用本地通知设置应用程序图标上的提醒个数。通过这些技巧,可以提升应用的用户体验。 ... [详细]
  • 本教程详细介绍了如何使用 TensorFlow 2.0 构建和训练多层感知机(MLP)网络,涵盖回归和分类任务。通过具体示例和代码实现,帮助初学者快速掌握 TensorFlow 的核心概念和操作。 ... [详细]
  • 目录一、salt-job管理#job存放数据目录#缓存时间设置#Others二、returns模块配置job数据入库#配置returns返回值信息#mysql安全设置#创建模块相关 ... [详细]
  • 深入解析Java枚举及其高级特性
    本文详细介绍了Java枚举的概念、语法、使用规则和应用场景,并探讨了其在实际编程中的高级应用。所有相关内容已收录于GitHub仓库[JavaLearningmanual](https://github.com/Ziphtracks/JavaLearningmanual),欢迎Star并持续关注。 ... [详细]
  • 本题来自WC2014,题目编号为BZOJ3435、洛谷P3920和UOJ55。该问题描述了一棵不断生长的带权树及其节点上小精灵之间的友谊关系,要求实时计算每次新增节点后树上所有可能的朋友对数。 ... [详细]
  • 本文介绍如何使用MFC和ADO技术调用SQL Server中的存储过程,以查询指定小区在特定时间段内的通话统计数据。通过用户界面选择小区ID、开始时间和结束时间,系统将计算并展示小时级的通话量、拥塞率及半速率通话比例。 ... [详细]
  • 本文介绍如何从JSON格式的文件中提取数据并将其分配给Bash脚本中的变量。我们将探讨具体的命令和工具,帮助你高效地完成这一任务。 ... [详细]
  • Redux入门指南
    本文介绍Redux的基本概念和工作原理,帮助初学者理解如何使用Redux管理应用程序的状态。Redux是一个用于JavaScript应用的状态管理库,特别适用于React项目。 ... [详细]
  • 离线安装Grafana Cloudera Manager插件并监控CDH集群
    本文详细介绍如何离线安装Cloudera Manager (CM) 插件,并通过Grafana监控CDH集群的健康状况和资源使用情况。该插件利用CM提供的API接口进行数据获取和展示。 ... [详细]
  • 本章详细介绍SP框架中的数据操作方法,包括数据查找、记录查询、新增、删除、更新、计数及字段增减等核心功能。通过具体示例和详细解析,帮助开发者更好地理解和使用这些方法。 ... [详细]
  • 探讨ChatGPT在法律和版权方面的潜在风险及影响,分析其作为内容创造工具的合法性和合规性。 ... [详细]
  • 在寻找轻量级Ruby Web框架的过程中,您可能会遇到Sinatra和Ramaze。两者都以简洁、轻便著称,但它们之间存在一些关键区别。本文将探讨这些差异,并提供详细的分析,帮助您做出最佳选择。 ... [详细]
  • 本文探讨了一个场景:用户成功登录后,如何确保Master-Detail视图控制器以模态形式展示。 ... [详细]
  • 使用Swift 2.2创建我的第一个Xcode应用
    本文将指导您如何使用Xcode 6搭建并运行一个简单的iOS应用程序。从启动Xcode到执行首个应用,每个步骤都将详细介绍。 ... [详细]
  • 在使用 iOS 应用时,遇到网络请求错误是常见的问题。本文将探讨两种常见的错误代码 -1003 和 -1001,并提供详细的解释和解决方案。 ... [详细]
author-avatar
谁的围脖搞笑排行榜
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有