File tree Expand file tree Collapse file tree 2 files changed +24
-10
lines changed
examples/basic-host-swift/Sources/BasicHostApp Expand file tree Collapse file tree 2 files changed +24
-10
lines changed Original file line number Diff line number Diff line change @@ -183,7 +183,6 @@ struct ToolCallCard: View {
183183 let onRemove : ( ) -> Void
184184
185185 @State private var isInputExpanded = false
186- @State private var isTearingDown = false
187186
188187 var body : some View {
189188 VStack ( alignment: . leading, spacing: 8 ) {
@@ -200,7 +199,7 @@ struct ToolCallCard: View {
200199
201200 Spacer ( )
202201
203- if isTearingDown {
202+ if toolCallInfo . isTearingDown {
204203 HStack ( spacing: 4 ) {
205204 ProgressView ( ) . scaleEffect ( 0.6 )
206205 Text ( " Closing... " )
@@ -222,10 +221,7 @@ struct ToolCallCard: View {
222221 . foregroundColor ( . secondary)
223222 }
224223
225- Button {
226- isTearingDown = true
227- onRemove ( )
228- } label: {
224+ Button ( action: onRemove) {
229225 Image ( systemName: " xmark " )
230226 . font ( . caption)
231227 . foregroundColor ( . secondary)
@@ -290,8 +286,8 @@ struct ToolCallCard: View {
290286 . padding ( 10 )
291287 . background ( Color ( UIColor . secondarySystemBackground) )
292288 . cornerRadius ( 10 )
293- . opacity ( isTearingDown ? 0.5 : 1.0 )
294- . animation ( . easeInOut( duration: 0.2 ) , value: isTearingDown)
289+ . opacity ( toolCallInfo . isTearingDown ? 0.5 : 1.0 )
290+ . animation ( . easeInOut( duration: 0.2 ) , value: toolCallInfo . isTearingDown)
295291 }
296292
297293 private var stateColor : Color {
Original file line number Diff line number Diff line change @@ -288,6 +288,7 @@ class ToolCallInfo: ObservableObject, Identifiable {
288288 @Published var result : ToolResult ?
289289 @Published var state : ExecutionState = . calling
290290 @Published var error : String ?
291+ @Published var isTearingDown = false
291292
292293 init (
293294 serverName: String ,
@@ -486,15 +487,32 @@ class ToolCallInfo: ObservableObject, Identifiable {
486487
487488 /// Teardown the app bridge before removing the tool call
488489 func teardown( ) async {
490+ // Prevent double-tap
491+ guard !isTearingDown else { return }
492+ isTearingDown = true
493+
489494 if let bridge = appBridge {
490495 do {
491- _ = try await bridge. sendResourceTeardown ( )
496+ // Use a timeout so we don't wait forever if app is unresponsive
497+ try await withThrowingTaskGroup ( of: Void . self) { group in
498+ group. addTask {
499+ _ = try await bridge. sendResourceTeardown ( )
500+ }
501+ group. addTask {
502+ try await Task . sleep ( nanoseconds: 5_000_000_000 ) // 5 second timeout
503+ throw CancellationError ( )
504+ }
505+ // Wait for first to complete (either teardown or timeout)
506+ try await group. next ( )
507+ group. cancelAll ( )
508+ }
492509 } catch {
493- print ( " [Host] Teardown failed: \( error) " )
510+ print ( " [Host] Teardown failed or timed out : \( error) " )
494511 }
495512 await bridge. close ( )
496513 }
497514 appBridge = nil
515+ // Note: isTearingDown stays true - the card will be removed from the list
498516 }
499517}
500518
You can’t perform that action at this time.
0 commit comments