Depends on how much you can tolerate Objective-C code, the answer ranges from "impossible" (pure Swift), to "inelegant" (Swift with Objective-C casting), to "just use Objective-C types" (i.e. NSArray / NSMutableArray
).
Since Swift is a statically dispatched language, dynamic features are not its strong suit. Swift 4 added support for key path and allow you to do something like this (lifted from Martin R's answer):
extension Array {
mutating func sort<T: Comparable>(byKeyPath keyPath: KeyPath<Element, T>) {
sort(by: { $0[keyPath: keyPath] < $1[keyPath: keyPath] })
}
}
struct DataModel {
var title: String
var dollar: Double
}
var myArray = [DataModel(title: "A", dollar: 12), DataModel(title: "B", dollar: 10)]
let keyPath = \DataModel.dollar
myArray.sort(byKeyPath: keyPath)
But there is no way to construct the key path from String
. It must be known at compile time to ensure type safety.
This is something Objective-C really excels at due to its dynamic nature:
@objcMembers
class DataModel: NSObject {
var title: String
var dollar: Double
init(title: String, dollar: Double) {
self.title = title
self.dollar = dollar
}
}
let myArray = [DataModel(title: "A", dollar: 12), DataModel(title: "B", dollar: 10)]
let sortKey = "title"
let sortDescriptors = [NSSortDescriptor(key: sortKey, ascending: true)]
let sortedArray = (myArray as NSArray).sortedArray(using: sortDescriptors) as! [DataModel]
Of course if you keep myArray
as NSArray / NSMutableArray
, you can leave all the castings behind. But then you will be firmly in Objective-C land and lose access to Swift's Array
functions like map
, filter
, etc.