【问题标题】:When attaching thumbnails to notifications, where does the file go?将缩略图附加到通知时,文件在哪里?
【发布时间】:2017-12-08 20:53:49
【问题描述】:

我正在向丰富的通知添加缩略图。我像这样生成要用作附件的图像:

let url = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
let fileURL = url.appendingPathComponent("someImageName", isDirectory: false).appendingPathExtension("png")
do{
    try UIImagePNGRepresentation(image)?.write(to: fileURL)
}catch{
    print("Could not write: ", error)
}

这是成功的。如果我现在运行这段代码:

let content = try? FileManager.default.contentsOfDirectory(atPath: NSTemporaryDirectory())
print(content ?? "error")

它打印出这个:["someImageName.png"]

然后我创建附件,如下所示:

let attachment = try? UNNotificationAttachment(identifier: "image", url: url, options: nil)
let content = UNMutableNotificationContent()
content.title = "Test"
content.attachments = [attachment!]
let request = UNNotificationRequest(identifier: "someID", content:content, trigger: /*someTrigger*/)
UNUserNotificationCenter.current().add(request, withCompletionHandler:...)

这一切正常。通知已发送,附件已附加。但是如果我现在打印出我的临时文件夹的内容,它是空的!我之前保存的图像已从此文件夹中删除。

经过大量的试验和错误,我发现在我添加通知的时候图像就被删除了。为什么我的通知会删除我的图像?我找不到这方面的任何文档!

如果我执行上述所有操作,但注释掉这一行://content.attachments = [attachment!],这意味着我存储的图像不会作为附件添加到我的通知中,那么我的图像不会从临时文件夹中删除!

这些文件在使用后是否被清理?还是在通知消失后我必须查看不同的文件夹并手动清理它们?我不希望我的应用创建数百张永远不会再次使用的图像,而无法删除它们..

【问题讨论】:

    标签: ios swift notifications uilocalnotification unnotificationattachment


    【解决方案1】:

    这些天我面临同样的问题,答案在文档中,乍一看不是很容易看到,但它就在那里:

    关于UNNotificationAttachment

    ...一旦验证,附件将被移动到附件数据中 存储,以便所有适当的人都可以访问它们 进程。

    关于init方法:

    当您安排包含附件的通知请求时, 附件的文件被移动到一个新的位置以方便访问 通过适当的过程。搬家后的唯一途径 该文件正在使用 UNUserNotificationCenter 对象的方法。

    注意:

    位于应用程序包内的附件会被复制 而不是移动。

    所以我决定:

    像您一样编写文件并在NSTemporaryDirectory() 中编写了一份副本我使用此示例轻松创建和删除 TempDirectory (Swift 4.0)(感谢 Ole Begemman):

    import Foundation
    
    /// A wrapper around a temporary file in a temporary directory. The directory
    /// has been especially created for the file, so it's safe to delete when you're
    /// done working with the file.
    ///
    /// Call `deleteDirectory` when you no longer need the file.
    struct TemporaryFile {
        let directoryURL: URL
        let fileURL: URL
        /// Deletes the temporary directory and all files in it.
        let deleteDirectory: () throws -> Void
        
        /// Creates a temporary directory with a unique name and initializes the
        /// receiver with a `fileURL` representing a file named `filename` in that
        /// directory.
        ///
        /// - Note: This doesn't create the file!
        init(creatingTempDirectoryForFilename filename: String) throws {
            let (directory, deleteDirectory) = try FileManager.default
                .urlForUniqueTemporaryDirectory()
            self.directoryURL = directory
            self.fileURL = directory.appendingPathComponent(filename)
            self.deleteDirectory = deleteDirectory
        }
    }
    
    extension FileManager {
        /// Creates a temporary directory with a unique name and returns its URL.
        ///
        /// - Returns: A tuple of the directory's URL and a delete function.
        ///   Call the function to delete the directory after you're done with it.
        ///
        /// - Note: You should not rely on the existence of the temporary directory
        ///   after the app is exited.
        func urlForUniqueTemporaryDirectory(preferredName: String? = nil) throws
            -> (url: URL, deleteDirectory: () throws -> Void)
        {
            let basename = preferredName ?? UUID().uuidString
            
            var counter = 0
            var createdSubdirectory: URL? = nil
            repeat {
                do {
                    let subdirName = counter == 0 ? basename : "\(basename)-\(counter)"
                    let subdirectory = temporaryDirectory
                        .appendingPathComponent(subdirName, isDirectory: true)
                    try createDirectory(at: subdirectory, withIntermediateDirectories: false)
                    createdSubdirectory = subdirectory
                } catch CocoaError.fileWriteFileExists {
                    // Catch file exists error and try again with another name.
                    // Other errors propagate to the caller.
                    counter += 1
                }
            } while createdSubdirectory == nil
            
            let directory = createdSubdirectory!
            let deleteDirectory: () throws -> Void = {
                try self.removeItem(at: directory)
            }
            return (directory, deleteDirectory)
        }
    }
    

    使用 TempURL 附加文件后,文件被移动,您可以删除 TempDirectory。

    后来进行了一些测试,可以肯定的是,我终于发现我的文件(在我的例子中是一个图像)存储在这里:

    URL: file:///var/mobile/Library/SpringBoard/PushStore/Attachments/com.YourApp.Name/8e52c6673f6e735f83535486bf750ba1118a3ade.png
    

    提交或清理通知中心后,文件被删除是的。

    为什么会这样? -> 沙盒!当我将文件附加到触发器时(此处为 UNCalendarNotificationTrigger),我将负责我的文件的日历命名为在通知中显示它,但要处理它,它必须将文件放在 HIS 目录中。

    希望对您有所帮助,我花了一段时间才发现所有这些谜团!

    【讨论】:

    • 为了确保我理解,您的重点是:验证后,附件将被移动到附件数据存储中,以便所有适当的进程都可以访问它们 对吧?基本上通知被移动了。您不太关心引用的其余部分:位于应用程序包内的附件被复制而不是移动。对吗?
    • 对,因为在我的情况下,附件是用户拍摄的照片,而不是我的 Bundle 中的文件存储。我已经编辑了我的答案。
    • 那么我们是否应该删除我们决定保存通知图像的临时文件夹,或者我们可以忘记它,因为系统会在通知触发后立即为我们清理?
    • @Citizen_5 是的,完成后将其删除。根据 Apple 的说法:“虽然这些文件没有备份到 iCloud,但请记住在完成后删除这些文件,以免它们继续占用用户设备上的空间”。来源:developer.apple.com/icloud/documentation/data-storage
    猜你喜欢
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 2013-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多