map (keyPath) di mana keyPath adalah variabel
let arr = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
arr.map(\.0) // [1, 2, 3, 4, 5]
Bekerja dengan baik. Tetapi kode di bawah ini tidak dapat dikompilasi:
let keyPath = \(Int, Int).0
arr.map(keyPath)
Tidak dapat mengonversi nilai tipe 'WritableKeyPath <(Int, Int), Int>' menjadi tipe argumen yang diharapkan '((Int, Int)) throws -> T'.
Parameter umum 'T' tidak dapat disimpulkan.
Jawaban
Array.mapmengharapkan penutupan dengan tanda tangan (Element) throws -> T.
Di Swift 5.2, jalur kunci diizinkan untuk diteruskan sebagai fungsi / penutupan (ini adalah proposal evolusi ), tetapi hanya sebagai literal (setidaknya, menurut proposal, dikatakan "untuk saat ini", jadi mungkin pembatasan ini akan dicabut ).
Untuk mengatasinya, Anda dapat membuat ekstensi di Sequenceyang menerima jalur kunci:
extension Sequence {
func map<T>(_ keyPath: KeyPath<Element, T>) -> [T] {
return map { $0[keyPath: keyPath] }
}
}
(kredit untuk: https://www.swiftbysundell.com/articles/the-power-of-key-paths-in-swift/)
Kemudian Anda dapat melakukan apa yang Anda inginkan:
let keyPath = \(Int, Int).0
arr.map(keyPath)
Proposal evolusi menunjukkan cara melakukannya dengan operator, tetapi Anda juga dapat menggunakan sintaks yang sama []atau ()apakah diterapkan sebagian atau tidak, karena subskrip dan fungsi tidak memerlukan argumen.
let oneTo5 = 1...5
let keyPath = \(Int, Int).0
XCTAssert(
zip(oneTo5, oneTo5).map(keyPath[]).elementsEqual(oneTo5)
)
let keyPath = \Double.isZero
XCTAssertFalse(keyPath[1.0]())
public extension KeyPath {
/// Convert a `KeyPath` to a partially-applied get accessor.
subscript() -> (Root) -> Value {
{ $0[keyPath: self] } } /// Convert a `KeyPath` to a get accessor. subscript(root: Root) -> () -> Value { { root[keyPath: self] } } } public extension ReferenceWritableKeyPath { /// Convert a `KeyPath` to a partially-applied get/set accessor pair. subscript() -> (Root) -> Computed<Value> { { self[$0] }
}
/// Convert a `KeyPath` to a get/set accessor pair.
subscript(root: Root) -> Computed<Value> {
.init(
get: self[root],
set: { root[keyPath: self] = $0 }
)
}
}
/// A workaround for limitations of Swift's computed properties.
///
/// Limitations of Swift's computed property accessors:
/// 1. They are not mutable.
/// 2. They cannot be referenced as closures.
@propertyWrapper public struct Computed<Value> {
public typealias Get = () -> Value
public typealias Set = (Value) -> Void
public init(
get: @escaping Get,
set: @escaping Set
) {
self.get = get
self.set = set
}
public var get: Get
public var set: Set
public var wrappedValue: Value {
get { get() }
set { set(newValue) }
}
public var projectedValue: Self {
get { self }
set { self = newValue }
}
}
//MARK:- public
public extension Computed {
init(
wrappedValue: Value,
get: @escaping Get = {
fatalError("`get` must be assigned before accessing `wrappedValue`.")
},
set: @escaping Set
) {
self.init(get: get, set: set)
self.wrappedValue = wrappedValue
}
}