Created
December 28, 2019 17:52
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} | |
) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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