@@ -9,14 +9,11 @@ import SwiftUI
99import ClassDump
1010
1111struct ContentView : View {
12- @StateObject private var listings : RuntimeListings = . shared
13-
1412 @State private var selectedObject : RuntimeObjectType ?
1513
1614 var body : some View {
1715 NavigationSplitView {
1816 ContentRootView ( selectedObject: $selectedObject)
19- . environmentObject ( listings)
2017 } detail: {
2118 if let selectedObject {
2219 NavigationStack {
@@ -40,34 +37,86 @@ private enum RuntimeTypeSearchScope: Hashable {
4037 case protocols
4138}
4239
43- struct ContentRootView : View {
44- @EnvironmentObject private var listings : RuntimeListings
40+ private extension RuntimeTypeSearchScope {
41+ var includesClasses : Bool {
42+ switch self {
43+ case . all: true
44+ case . classes: true
45+ case . protocols: false
46+ }
47+ }
48+ var includesProtocols : Bool {
49+ switch self {
50+ case . all: true
51+ case . classes: false
52+ case . protocols: true
53+ }
54+ }
55+ }
56+
57+ private class ContentRootViewModel : ObservableObject {
58+ let runtimeListings : RuntimeListings = . shared
4559
46- @Binding var selectedObject : RuntimeObjectType ?
47- @State private var searchString : String = " "
48- @State private var searchScope : RuntimeTypeSearchScope = . all
60+ @Published var searchString : String
61+ @Published var searchScope : RuntimeTypeSearchScope
62+
63+ @Published private( set) var runtimeObjects : [ RuntimeObjectType ] // filtered based on search
4964
50- private var runtimeObjects : [ RuntimeObjectType ] {
65+ private static func runtimeObjectsFor ( classNames : [ String ] , protocolNames : [ String ] , searchString : String , searchScope : RuntimeTypeSearchScope ) -> [ RuntimeObjectType ] {
5166 var ret : [ RuntimeObjectType ] = [ ]
52- if searchScope != . protocols {
53- ret += listings . classList . map { . class( named: $0) }
67+ if searchScope. includesClasses {
68+ ret += classNames . map { . class( named: $0) }
5469 }
55- if searchScope != . classes {
56- ret += listings . protocolList . map { . protocol( named: $0) }
70+ if searchScope. includesProtocols {
71+ ret += protocolNames . map { . protocol( named: $0) }
5772 }
5873 if searchString. isEmpty { return ret }
5974 return ret. filter { $0. name. localizedCaseInsensitiveContains ( searchString) }
6075 }
6176
77+ init ( ) {
78+ let searchString = " "
79+ let searchScope : RuntimeTypeSearchScope = . all
80+
81+ self . searchString = searchString
82+ self . searchScope = searchScope
83+ self . runtimeObjects = Self . runtimeObjectsFor (
84+ classNames: runtimeListings. classList, protocolNames: runtimeListings. protocolList,
85+ searchString: searchString, searchScope: searchScope
86+ )
87+
88+ let debouncedSearch = $searchString
89+ . debounce ( for: 0.08 , scheduler: RunLoop . main)
90+
91+ $searchScope
92+ . combineLatest ( debouncedSearch, runtimeListings. $classList, runtimeListings. $protocolList) {
93+ Self . runtimeObjectsFor (
94+ classNames: $2, protocolNames: $3,
95+ searchString: $1, searchScope: $0
96+ )
97+ }
98+ . assign ( to: & $runtimeObjects)
99+ }
100+ }
101+
102+ struct ContentRootView : View {
103+ @StateObject private var viewModel : ContentRootViewModel
104+ @Binding var selectedObject : RuntimeObjectType ?
105+
106+ init ( selectedObject: Binding < RuntimeObjectType ? > ) {
107+ _viewModel = StateObject ( wrappedValue: ContentRootViewModel ( ) )
108+ _selectedObject = selectedObject
109+ }
110+
62111 var body : some View {
63112 NavigationStack {
64- let runtimeObjects = self . runtimeObjects
113+ let runtimeObjects = viewModel . runtimeObjects
65114 ListView ( runtimeObjects, selection: $selectedObject) { runtimeObject in
66115 RuntimeObjectRow ( type: runtimeObject)
67116 }
68117 . id ( runtimeObjects) // don't try to diff the List
69- . searchable ( text: $searchString)
70- . searchScopes ( $searchScope) {
118+ . searchable ( text: $viewModel . searchString)
119+ . searchScopes ( $viewModel . searchScope) {
71120 Text ( " All " )
72121 . tag ( RuntimeTypeSearchScope . all)
73122 Text ( " Classes " )
@@ -88,7 +137,7 @@ struct ContentRootView: View {
88137 ImageClassPicker ( namedNode: namedNode, selection: $selectedObject)
89138 } else {
90139 NamedNodeView ( node: namedNode)
91- . environmentObject ( listings )
140+ . environmentObject ( RuntimeListings . shared )
92141 }
93142 }
94143 }
0 commit comments