Подтвердить что ты не робот

Swift 2 iOS - получить список файлов, отсортированный по дате создания - более сжатое решение?

В моем коде, который работает, я возвращаю [String]?, содержащий имена файлов (lastPathComponent), хранящихся в /Documents/ - упорядоченных по дате последнего изменения.

Я считаю, что, вероятно, я использую слишком много шагов, и я ищу совет, как уменьшить код.

Для достижения требуемого результата в настоящее время я создаю два промежуточных словаря: var attributesDictionary: [String : AnyObject]? и var urlDictionary = [NSURL:NSDate](). Цикл через начальный [NSURL] Я использую два шага - .resourceValuesForKeys инициализирует attributesDictionary. Затем я заполняю urlDictionary, чтобы он содержал URL-адрес и значение для ключа NSURLContentModificationDateKey.

Я уверен, что должен быть способ достичь этого результата без создания urlDictionary и attributesDictionary и без необходимости в цикле. Возможно, из urlArray напрямую. Вот мой текущий код:

EDIT: do{} не требовались, как указал Артур Геворкян в первом комментарии.

func getFileList() -> [String]? {
    let directory = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
    let properties = [NSURLLocalizedNameKey, NSURLCreationDateKey, NSURLContentModificationDateKey, NSURLLocalizedTypeDescriptionKey]

    // no catch required - contentsOfDirectoryAtURL returns nil if there is an error
    if let urlArray = try? NSFileManager.defaultManager().contentsOfDirectoryAtURL(directory, includingPropertiesForKeys: properties, options:NSDirectoryEnumerationOptions.SkipsHiddenFiles) {
        var attributesDictionary: [String:AnyObject]?
        var dateLastModified: NSDate
        var urlDictionary = [NSURL:NSDate]()

        for URLs in urlArray {
            // no catch required - resourceValuesForKeys returns nil if there is an error
            attributesDictionary = try? URLs.resourceValuesForKeys(properties)
            dateLastModified = attributesDictionary?[NSURLContentModificationDateKey] as! NSDate
            urlDictionary[URLs] = dateLastModified
        }
        // this approach to sort is used because NSDate cannot be directly compared with </>
        return urlDictionary.filter{$0 != nil}.sort{$0.1.compare($1.1) == NSComparisonResult.OrderedDescending }.map{$0.0}.map{$0.lastPathComponent!}
    } else {
        return nil
    }
}
4b9b3361

Ответ 1

Возможное решение:

if let urlArray = try? NSFileManager.defaultManager().contentsOfDirectoryAtURL(directory,
    includingPropertiesForKeys: properties, options:.SkipsHiddenFiles) {

    return urlArray.map { url -> (String, NSTimeInterval) in
        var lastModified : AnyObject?
        _ = try? url.getResourceValue(&lastModified, forKey: NSURLContentModificationDateKey)
        return (url.lastPathComponent!, lastModified?.timeIntervalSinceReferenceDate ?? 0)
    }
    .sort({ $0.1 > $1.1 }) // sort descending modification dates
    .map { $0.0 } // extract file names

} else {
    return nil
}

Массив URL-адресов сначала сопоставляется с массивом (lastPathComponent, lastModificationDate) кортежей, затем сортируется в соответствии с дату последней модификации и, наконец, извлеченное имя пути.

attributesDictionary можно избежать, используя getResourceValue(_ : forKey), чтобы получить только последнюю дату изменения.

Обновление для Swift 3:

let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
if let urlArray = try? FileManager.default.contentsOfDirectory(at: directory,
                                                               includingPropertiesForKeys: [.contentModificationDateKey],
                                                               options:.skipsHiddenFiles) {

    return urlArray.map { url in
            (url.lastPathComponent, (try? url.resourceValues(forKeys: [.contentModificationDateKey]))?.contentModificationDate ?? Date.distantPast)
        }
        .sorted(by: { $0.1 > $1.1 }) // sort descending modification dates
        .map { $0.0 } // extract file names

} else {
    return nil
}

Ответ 2

Код Swift 3 с полным решением: На основе ответа @ingconti. Этот метод возвращает список имен элементов из предоставленного пути URL.

func filesSortedList(atPath: URL) -> [String]? {

    var fileNames = [String]()
    let keys = [URLResourceKey.contentModificationDateKey]

    guard let fullPaths = try? FileManager.default.contentsOfDirectory(at: atPath, includingPropertiesForKeys:keys, options: FileManager.DirectoryEnumerationOptions.skipsHiddenFiles) else {
        return [""]
    }

    let orderedFullPaths = fullPaths.sorted(by: { (url1: URL, url2: URL) -> Bool in
        do {
            let values1 = try url1.resourceValues(forKeys: [.creationDateKey, .contentModificationDateKey])
            let values2 = try url2.resourceValues(forKeys: [.creationDateKey, .contentModificationDateKey])

            if let date1 = values1.creationDate, let date2 = values2.creationDate {
                //if let date1 = values1.contentModificationDate, let date2 = values2.contentModificationDate {
                return date1.compare(date2) == ComparisonResult.orderedDescending
            }
        } catch _{

        }
        return true
    })

    for fileName in orderedFullPaths {
        do {
            let values = try fileName.resourceValues(forKeys: [.creationDateKey, .contentModificationDateKey])
            if let date = values.creationDate{
                //let date : Date? = values.contentModificationDate
                print(fileName.lastPathComponent, " ", date)
                let theFileName = fileName.lastPathComponent
                fileNames.append(theFileName)
            }
        }
        catch _{

        }
    }
    return fileNames
}

Ответ 3

return urlDictionary.filter{$0 != nil}.sort{$0.1.compare($1.1) == NSComparisonResult.OrderedDescending }.map{$0.0}.map{$0.lastPathComponent!}

определенно является избыточной линией кода:) Вы можете пропустить пару шагов фильтра/карты, используя другой метод NSFileManager:

func contentsOfDirectoryAtPath(_ path: String) throws -> [String] 

и

func attributesOfItemAtPath(_ path: String) throws -> [String : AnyObject].

В конце концов, вы получите что-то эквивалентное тому, что вы уже сделали. Я думаю, что ваш код немного сложный, но подход неплохой.

Ответ 4

Swift 3, iOS 10

// MARK: - String

extension String {
    func stringByAppendingPathComponent(path: String) -> String {

        let nsSt = self as NSString

        return nsSt.appendingPathComponent(path)
    }
}

// MARK: - File manager
    let fileManagerController = FileManager.default


// MARK: - Application Document Directory
// NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] return an array of String, we will catch just the first item from array

    let applicationDocumentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]


// MARK: - File path
// Combined with helpers from String extension

    let filePath =  applicationDocumentsDirectory.stringByAppendingPathComponent(path: name)

     do {
         let atributes = try fileManagerController.attributesOfItem(atPath: filePath)
            // File creation date
            if let fileDate = atributes[FileAttributeKey.creationDate] as? Date {

               // fileDate has a String value
               print(fileDate)
             // Will print a creation date of file


            }   
      } catch let error as NSError {
            print("Failure to know creation date \(error.description)")
      }

Ответ 5

Swift 3.0 Sierra 10.12

Получение отсортированного массива на дату (создание или изменение):

func enumAndSortFilesAt(path: String){

    let fm = FileManager.default
    let url = URL(fileURLWithPath: path)

    let optionMask: FileManager.DirectoryEnumerationOptions = [
        .skipsHiddenFiles
    ]
    let keys = [URLResourceKey.contentModificationDateKey]

    guard let files = try? fm.contentsOfDirectory(at: url,
                                                  includingPropertiesForKeys : keys,
                                                  options: optionMask ) else {

                                                    return
    }

    let ordered = files.sorted { ( u1: URL, u2: URL) -> Bool in


        do{
         let values1 = try u1.resourceValues(forKeys: [.creationDateKey, .contentModificationDateKey])
         let values2 = try u2.resourceValues(forKeys: [.creationDateKey, .contentModificationDateKey])

//          if let date1 = values1.creationDate, let date2 = values2.creationDate {
            if let date1 = values1.contentModificationDate, let date2 = values2.contentModificationDate {
            return date1.compare(date2) == ComparisonResult.orderedAscending
            }
        }catch _{
        }

        return true
    }


    for f in ordered {
        do {

            let values = try f.resourceValues(forKeys: [.creationDateKey, .contentModificationDateKey])

            if let date = values.creationDate{
                //let date : Date? = values.contentModificationDate
                print(f.lastPathComponent, " ", date)
            }
        }
        catch _{

        }
    }


}

Ответ 6

Swift 5.0. Простое расширение на основе предыдущих ответов:

extension FileManager {

enum ContentDate {
    case created, modified, accessed

    var resourceKey: URLResourceKey {
        switch self {
        case .created: return .creationDateKey
        case .modified: return .contentModificationDateKey
        case .accessed: return .contentAccessDateKey
        }
    }
}

func contentsOfDirectory(atURL url: URL, sortedBy: ContentDate, ascending: Bool = true, options: FileManager.DirectoryEnumerationOptions = [.skipsHiddenFiles]) throws -> [String]? {

    let key = sortedBy.resourceKey

    var files = try contentsOfDirectory(at: url, includingPropertiesForKeys: [key], options: options)

    try files.sort {

        let values1 = try $0.resourceValues(forKeys: [key])
        let values2 = try $1.resourceValues(forKeys: [key])

        if let date1 = values1.allValues.first?.value as? Date, let date2 = values2.allValues.first?.value as? Date {

            return date1.compare(date2) == (ascending ? .orderedAscending : .orderedDescending)
        }
        return true
    }
    return files.map { $0.lastPathComponent }
}

}