sorted(by:) with a closure that compares the property: images.sorted { $0.fileID < $1.fileID }. Use sort(by:) instead if you want to reorder the existing variable in place.Why this happens
The compiler can only sort an array when it knows what type the elements are. In the original code, var images : Array = [] uses the generic Array type without its element type. Modern Swift rejects that with a generic-argument error, and if the array becomes [Any], the closure cannot see fileID at all. You may see Value of type 'Any' has no member 'fileID' when you try to compare elements.
An empty array literal also needs a type from somewhere. If you write var images = [] and only append objects later, Swift still needs an explicit element type at the declaration. Declaring [ImageFile] fixes both problems.
There is also a common mix-up between two methods. sorted(by:) returns a new array and leaves the original alone. sort(by:) reorders the array in place and requires a var. Both take a closure that returns true when the first element should come before the second.
How to fix it
1. Declare the array as [ImageFile]
Start with a concrete element type. That gives the compiler the property names available on each element.
struct ImageFile {
var fileName: String
var fileID: Int
}
var images: [ImageFile] = [
ImageFile(fileName: "image1.png", fileID: 101),
ImageFile(fileName: "image2.png", fileID: 202)
]
If ImageFile is a class, the array type is still [ImageFile]. Sorting moves references around; it does not change the objects themselves.
2. Sort ascending with sorted(by:)
Use < for ascending order:
let ascending = images.sorted { $0.fileID < $1.fileID }
ascending is a new array. images keeps its original order. The trailing closure is the by: argument, so images.sorted(by: { $0.fileID < $1.fileID }) is the same call written out.
3. Sort descending
Use > instead:
let descending = images.sorted { $0.fileID > $1.fileID }
Do not use <= or >= in the closure. A sort comparator must be a strict ordering; equal elements must return false in both directions.
4. Sort the existing array in place
If you want to change images itself, call sort(by:):
images.sort { $0.fileID < $1.fileID }
This requires images to be declared with var. On a let constant you will get cannot use mutating member on immutable value: 'images' is a 'let' constant.
5. Use a named comparator for reuse
If the same order is needed in several places, pull the closure out:
func sorterForFileIDASC(_ a: ImageFile, _ b: ImageFile) -> Bool {
a.fileID < b.fileID
}
let sorted = images.sorted(by: sorterForFileIDASC)
6. Add a tie-breaker
When two objects have the same fileID, the order between them is not defined by that comparison alone. Compare a second property to make the result predictable:
let sorted = images.sorted {
if $0.fileID != $1.fileID {
return $0.fileID < $1.fileID
}
return $0.fileName < $1.fileName
}
For properties that are all Comparable, a tuple comparison is shorter:
let sorted = images.sorted {
($0.fileID, $0.fileName) < ($1.fileID, $1.fileName)
}
7. Give the type a natural order with Comparable
If sorting by fileID is the normal order for ImageFile, conform to Comparable:
struct ImageFile: Comparable {
var fileName: String
var fileID: Int
static func < (lhs: ImageFile, rhs: ImageFile) -> Bool {
lhs.fileID < rhs.fileID
}
}
let sorted = images.sorted()
Only do this when the type has one obvious default order. Otherwise keep the closure at the call site so the intent is visible.
8. Handle an optional property
If fileID is Int?, unwrap before comparing:
let sorted = images.sorted {
($0.fileID ?? 0) < ($1.fileID ?? 0)
}
Choose a fallback that matches your data. Do not force-unwrap inside the comparator; one nil value will crash the sort.
How to prevent it
- Declare arrays as
[ImageFile], not bareArrayor[Any]. - Use
<or>in comparators, never<=or>=. - Use
sorted(by:)for a new array andsort(by:)for in-place changes. - Add a tie-breaker when equal property values should have a stable, predictable order.
- Extract a named comparator or add
Comparablewhen the same sort is repeated.
FAQ
Does sorted(by:) modify the original array?
No. sorted(by:) returns a new sorted array. To reorder the existing array, use sort(by:) on a var.
Can I sort by a String property?
Yes. String is Comparable, so images.sorted { $0.fileName < $1.fileName } works. For a case-insensitive sort, compare with localizedCaseInsensitiveCompare and check for .orderedAscending.
Why does a comparator with <= cause problems?
A sort comparator must return true only when the first element strictly belongs before the second. Using <= makes equal elements compare as both before and after each other, which can produce an invalid order or unexpected results.
Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.
