Skip to content

Instantly share code, notes, and snippets.

@jemmons
Created December 28, 2019 17:52
Show Gist options
  • Save jemmons/3b0f9442e75f9126f9f4082ae4b49fa7 to your computer and use it in GitHub Desktop.
Save jemmons/3b0f9442e75f9126f9f4082ae4b49fa7 to your computer and use it in GitHub Desktop.
Allows us to filter `Binding`s while keeping everything bindable — so long as they wrap collections of `Identifiable` elements.
import SwiftUI
extension Binding where Value: MutableCollection, Value: RangeReplaceableCollection, Value.Element: Identifiable {
func filter(_ isIncluded: @escaping (Value.Element)->Bool) -> Binding<[Value.Element]> {
return Binding<[Value.Element]>(
get: {
// The binding returns a filtered subset of the original wrapped collection.
self.wrappedValue.filter(isIncluded)
},
set: { newValue in
// Assignments to the binding's wrapped value are compared with IDs in the original biding's collection.
// If they match, they are replaced.
// If no match is found, they are appended.
newValue.forEach { newItem in
guard let i = self.wrappedValue.firstIndex(where: { $0.id == newItem.id }) else {
self.wrappedValue.append(newItem)
return
}
self.wrappedValue[i] = newItem
}
}
)
}
}
// MARK: - CONTENT VIEW
struct ContentView: View {
@ObservedObject var model: AppModel = AppModel()
var body: some View {
NavigationView {
List {
NavigationLink(destination: TodosView(todos: $model.todos)) {
Text("All Todos")
}
// This important part is right here ⤵️
NavigationLink(destination: TodosView(todos: $model.todos.filter { $0.isCritical })) {
Text("Critical Todos")
}
}
}
}
}
// MARK: - OTHER SUBVIEWS
struct TodosView: View {
@Binding var todos: [Todo]
var body: some View {
List {
ForEach(todos.indices) { i in
TodoView(todo: self.todos[i])
.onTapGesture {
// Note we can still propegate updates, even though it’s (potentially) a subset.
self.todos[i].isComplete.toggle()
}
}
}
}
}
struct TodoView: View {
let todo: Todo
var body: some View {
HStack {
Image(systemName: todo.isComplete ? "checkmark.square.fill" : "square")
Text(todo.title)
}
}
}
// MARK: - MODELS AND STUFF
struct Todo: Identifiable {
let id = UUID()
let title: String
let isCritical: Bool
var isComplete: Bool = false
init(title: String, isCritical: Bool = false) {
self.title = title
self.isCritical = isCritical
}
}
class AppModel: ObservableObject {
@Published var todos: [Todo] = [
Todo(title: "Write blog post"),
Todo(title: "Write another blog post"),
Todo(title: "Seriously, there are like 30 blog posts I have to write"),
Todo(title: "post a quick gist", isCritical: true),
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment