Unordered Directory Contents

Since I updated to macOS 10.13 High Sierra, some of my unit tests broke. Examining the failures more carefully, I discovered that they were making assumptions about the order that Foundation’s FileManager.contentsOfDirectory(atPath:) would return items.

I wrote a quick playground to test the behavior on a 10.12 machine:

import Foundation

let array = try! FileManager.default.contentsOfDirectory(atPath: "/Applications/Utilities")
print("\(array.debugDescription)")

The results come back alphabetically ordered by file name:

[".DS_Store", ".localized", "Activity Monitor.app", "Adobe Flash Player Install Manager.app", "AirPort Utility.app", "Audio MIDI Setup.app", "Bluetooth File Exchange.app", "Boot Camp Assistant.app", "ColorSync Utility.app", "Console.app", "Digital Color Meter.app", "Disk Utility.app", "Grab.app", "Grapher.app", "Keychain Access.app", "Migration Assistant.app", "Script Editor.app", "System Information.app", "Terminal.app", "VoiceOver Utility.app"]

The same playground on 10.13 tells a different story:

["AirPort Utility.app", "VoiceOver Utility.app", "Terminal.app", "Activity Monitor.app", ".DS_Store", "Grapher.app", "Audio MIDI Setup.app", ".localized", "System Information.app", "Keychain Access.app", "Grab.app", "Migration Assistant.app", "Script Editor.app", "ColorSync Utility.app", "Console.app", "Disk Utility.app", "Bluetooth File Exchange.app", "Boot Camp Assistant.app", "Digital Color Meter.app"]

I thought at first this might have been related to the APFS conversion that 10.13 applied to my boot volume, but the same ordering discrepancy occurs for items on my HFS+ volumes as well.

After checking the 10.13 release notes for clues, and finding none, I consulted the documentation. Well, what do you know?

The order of the files in the returned array is undefined.

So, mea culpa. The test code in question probably shouldn’t have ever made assumptions about the ordering of items returned from this method. While it has evidently always been undefined, it appears they are only making good on that promise in 10.13. You have been warned!

Update: It turns out I have some real bugs in my apps, not just in my tests, because of assuming the results of this call will be reasonably sorted. Luckily I use a bottleneck method for obtaining the list of files, and I can impose my own sorting right at the source. If you’re looking to make the same kinds of changes to your app, be sure to heed Peter Maurer’s advice and use “localizedStandardCompare” (available since macOS10.6/iOS4) to obtain Finder-like ordering of the results.