Skip to content

Instantly share code, notes, and snippets.

@koher
Created January 9, 2021 10:49
Show Gist options
  • Save koher/095eed09a3f8369bd0e72dbd9e689a72 to your computer and use it in GitHub Desktop.
Save koher/095eed09a3f8369bd0e72dbd9e689a72 to your computer and use it in GitHub Desktop.
Extensions for `Binding` to make collections available with `ForEach` keeping bindings to their elements
import SwiftUI
extension Binding: Identifiable where Value: Identifiable {
public var id: Value.ID {
wrappedValue.id
}
}
extension Binding: Sequence where Value: MutableCollection, Value: RandomAccessCollection, Value.Element: Identifiable {
public typealias Element = Binding<Value.Element>
public func makeIterator() -> BindingIterator {
BindingIterator(collection: self)
}
public struct BindingIterator: IteratorProtocol {
private let collection: Binding<Value>
private var indices: Value.Indices.SubSequence
fileprivate init(collection: Binding<Value>) {
self.collection = collection
self.indices = collection.wrappedValue.indices[...]
}
public mutating func next() -> Binding<Value.Element>? {
guard let index = indices.popFirst() else { return nil }
return collection[index]
}
}
}
extension Binding: Collection where Value: MutableCollection, Value: RandomAccessCollection, Value.Element: Identifiable {
public typealias Index = Value.Index
public typealias Indices = Value.Indices
public typealias SubSequence = Binding<Value.SubSequence>
public subscript(index: Value.Index) -> Binding<Value.Element> {
get {
var element0 = wrappedValue[index]
let id = element0.id
return Binding<Value.Element>(
get: {
let element = wrappedValue[index]
if element.id == id {
element0 = element
return element
} else {
if let element = wrappedValue.first(where: { $0.id == id }) {
element0 = element
return element
} else {
// Dummy value for dangling bindings
return element0
}
}
},
set: { newValue in
let element = wrappedValue[index]
if element.id == id {
wrappedValue[index] = newValue
} else {
if let index = wrappedValue.firstIndex(where: { $0.id == id }) {
wrappedValue[index] = newValue
} else {
// Ignores changes through dangling bindings
}
}
}
)
}
}
public var indices: Value.Indices {
wrappedValue.indices
}
public var startIndex: Value.Index {
wrappedValue.startIndex
}
public var endIndex: Value.Index {
wrappedValue.endIndex
}
public func index(after i: Value.Index) -> Value.Index {
wrappedValue.index(after: i)
}
}
extension Binding: BidirectionalCollection where Value: MutableCollection, Value: RandomAccessCollection, Value.Element: Identifiable {
public func index(before i: Value.Index) -> Value.Index {
wrappedValue.index(before: i)
}
}
extension Binding: RandomAccessCollection where Value: MutableCollection, Value: RandomAccessCollection, Value.Element: Identifiable {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment