@@ -103,7 +103,7 @@ final class RepoViewModel: ObservableObject {
103103 }
104104}
105105
106- // MARK: - The SwiftUI View (searchable apps list )
106+ // MARK: - The SwiftUI View (search bar added; works without NavigationView )
107107public struct AppsView : View {
108108 @StateObject private var vm : RepoViewModel
109109
@@ -131,41 +131,61 @@ public struct AppsView: View {
131131 }
132132
133133 public var body : some View {
134- Group {
135- if vm. isLoading && vm. apps. isEmpty {
136- VStack ( spacing: 12 ) {
137- ProgressView ( )
138- Text ( " Loading apps... " )
139- . font ( . subheadline)
140- . foregroundColor ( . secondary)
141- }
142- . padding ( )
143- } else if let error = vm. errorMessage, vm. apps. isEmpty {
144- VStack ( spacing: 12 ) {
145- Text ( " Error " )
146- . font ( . headline)
147- Text ( error)
148- . font ( . subheadline)
149- . foregroundColor ( . secondary)
150- . multilineTextAlignment ( . center)
151- Button ( " Retry " ) { vm. refresh ( ) }
152- . padding ( . top, 8 )
153- }
154- . padding ( )
155- } else {
156- // List with searchable modifier
157- List ( filteredApps) { app in
158- AppRowView ( app: app)
134+ VStack ( spacing: 0 ) {
135+ // Search bar (always visible, works outside NavigationView)
136+ HStack {
137+ Image ( systemName: " magnifyingglass " )
138+ . foregroundColor ( . secondary)
139+ TextField ( " Search apps, developer or bundle ID " , text: $searchText)
140+ . textInputAutocapitalization ( . never)
141+ . disableAutocorrection ( true )
142+ . focused ( $searchFieldFocused)
143+ . submitLabel ( . search)
144+ if !searchText. isEmpty {
145+ Button ( action: { searchText = " " } ) {
146+ Image ( systemName: " xmark.circle.fill " )
147+ . foregroundColor ( . secondary)
148+ }
149+ . buttonStyle ( . plain)
159150 }
160- . listStyle ( PlainListStyle ( ) )
161- . refreshable { vm. refresh ( ) }
162- . searchable ( text: $searchText, placement: . automatic, prompt: " Search apps or developer " )
163- . onSubmit ( of: . search) {
164- // Dismiss keyboard on submit
165- searchFieldFocused = false
151+ }
152+ . padding ( 10 )
153+ . background ( . regularMaterial) // nice subtle background
154+ . cornerRadius ( 10 )
155+ . padding ( . horizontal)
156+ . padding ( . top, 8 )
157+
158+ // Content
159+ Group {
160+ if vm. isLoading && vm. apps. isEmpty {
161+ VStack ( spacing: 12 ) {
162+ ProgressView ( )
163+ Text ( " Loading apps... " )
164+ . font ( . subheadline)
165+ . foregroundColor ( . secondary)
166+ }
167+ . padding ( )
168+ } else if let error = vm. errorMessage, vm. apps. isEmpty {
169+ VStack ( spacing: 12 ) {
170+ Text ( " Error " )
171+ . font ( . headline)
172+ Text ( error)
173+ . font ( . subheadline)
174+ . foregroundColor ( . secondary)
175+ . multilineTextAlignment ( . center)
176+ Button ( " Retry " ) { vm. refresh ( ) }
177+ . padding ( . top, 8 )
178+ }
179+ . padding ( )
180+ } else {
181+ List ( filteredApps) { app in
182+ AppRowView ( app: app)
183+ }
184+ . listStyle ( PlainListStyle ( ) )
185+ . refreshable { vm. refresh ( ) }
166186 }
167- . animation ( . default, value: filteredApps)
168187 }
188+ . padding ( . top, 8 )
169189 }
170190 . toolbar {
171191 ToolbarItem ( placement: . navigationBarTrailing) {
0 commit comments