SwiftUI自定义代理实现:连接视图与TestManager的方法
Got it, let's walk through how to connect your TestManager with the ContentView and handle delegate callbacks properly—with SwiftUI-specific tweaks for state management, since it works a bit differently than Storyboards.
First, there's a small parsing mismatch we need to fix first, then we'll wire up the delegate step by step.
First: Fix the Model Parsing Bug
I noticed your TestModel defines a text property, but in the parseJson method you're trying to access decodedData.message—this will cause decoding to fail (since NumbersAPI returns a text field). Let's fix that:
struct TestModel: Decodable { let text: String } // Update the parseJson method in TestManager: func parseJson(_ jsonData: Data) -> TestModel? { let decoder = JSONDecoder() do { let decodedData = try decoder.decode(TestModel.self, from: jsonData) let model = TestModel(text: decodedData.text) return model } catch { delegate?.didFailWithError(error) return nil } }
Step 1: Prepare the View for Delegate & State
SwiftUI views are value types (structs), so we need to use @StateObject to keep our TestManager instance alive across view refreshes, and @State to store the data we'll display. We'll also make ContentView conform to TestManagerDelegate.
Update your ContentView like this:
import SwiftUI struct ContentView: View { // Use @StateObject to retain the manager instance (avoids re-creating it on refresh) @StateObject private var manager = TestManager() // State to hold the trivia text we'll display @State private var triviaText = "Test Text" // State to show error messages if something goes wrong @State private var errorMessage: String? var body: some View { ZStack{ Color(.green) .edgesIgnoringSafeArea(.all) VStack { Text(triviaText) .font(.title) .foregroundColor(.white) .padding() Button(action: { // Trigger the API call when the button is tapped manager.Get() }) { Text("Get number 2") .font(.title) .foregroundColor(.white) .padding() .overlay(RoundedRectangle(cornerRadius: 30) .stroke(Color.white, lineWidth: 5)) } // Show error message if there's an error if let errorMessage = errorMessage { Text(errorMessage) .foregroundColor(.red) .padding() } } } // Set the delegate once the view is ready .onAppear { manager.delegate = self } } } // Make ContentView conform to TestManagerDelegate extension ContentView: TestManagerDelegate { func didCorrectlyComplete(_ testName: TestManager, model: TestModel) { // API calls run on background threads—update UI state on main thread DispatchQueue.main.async { triviaText = model.text errorMessage = nil } } func didFailWithError(_ error: Error) { // Update error state on main thread too DispatchQueue.main.async { errorMessage = error.localizedDescription } } }
Key SwiftUI-Specific Notes
- @StateObject: We use this for
managerbecause it preserves the instance across view re-renders. A regularvarwould create a newTestManagerevery time the view refreshes, breaking the delegate connection. - Main Thread Updates: API calls run in the background, so we must update UI state (
triviaText,errorMessage) on the main thread to avoid runtime warnings or glitches. - Delegate Assignment: We set
manager.delegate = selfin.onAppearto ensure the delegate is linked once the view is fully loaded.
Test It Out
Tap the "Get number 2" button, and the TestManager will make the API call. When the response comes back, the delegate methods will update the view's state—showing the trivia text or an error message if something goes wrong.
内容的提问来源于stack exchange,提问作者runemonster

