Key Paths, Dynamic Member Lookup and callAsFunction
29.1 Key Paths
Kinds: KeyPath (read-only), WritableKeyPath (value types), ReferenceWritableKeyPath (classes), PartialKeyPath, AnyKeyPath. Used by SwiftUI bindings, Combine assign, SwiftData predicates (#Predicate), and sorting APIs (KeyPathComparator).
struct User { var name: String; var age: Int; var address: Address }
struct Address { var city: String }
let nameKP = \User.name // KeyPath<User, String>
let cityKP = \User.address.city // WritableKeyPath (composed)
var user = User(name: "Ana", age: 30, address: Address(city: "Pune"))
print(user[keyPath: nameKP])
user[keyPath: cityKP] = "Mumbai"
let users = [user, User(name: "Bo", age: 25, address: Address(city: "Delhi"))]
users.map(\.name) // key paths as functions (SE-0249)
users.sorted { $0[keyPath: \.age] < $1[keyPath: \.age] }
extension Sequence {
func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>) -> [Element] {
sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
}
}
users.sorted(by: \.age)29.2 @dynamicMemberLookup
@dynamicMemberLookup
struct JSON {
private let storage: [String: Any]
init(_ dict: [String: Any]) { storage = dict }
subscript(dynamicMember key: String) -> JSON? {
(storage[key] as? [String: Any]).map(JSON.init)
}
subscript(dynamicMember key: String) -> String? { storage[key] as? String }
}
let json = JSON(["user": ["name": "Ana"]])
let name: String? = json.user?.name
@dynamicMemberLookup
struct Lens<T> { // key-path-based dynamic member lookup (type-safe)
var value: T
subscript<V>(dynamicMember keyPath: WritableKeyPath<T, V>) -> V {
get { value[keyPath: keyPath] }
set { value[keyPath: keyPath] = newValue }
}
}29.3 callAsFunction and @dynamicCallable
struct Polynomial {
let coefficients: [Double]
func callAsFunction(_ x: Double) -> Double {
coefficients.enumerated().reduce(0) { $0 + $1.element * pow(x, Double($1.offset)) }
}
}
let f = Polynomial(coefficients: [1, 2, 3]) // 1 + 2x + 3x²
f(2) // 17: instances callable like functions
@dynamicCallable
struct Logger {
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Any>) {
print(args.map { "\($0.key)=\($0.value)" }.joined(separator: " "))
}
}
let log = Logger()
log(level: "info", message: "started")