SwiftUI视图加载前从服务器获取数据并显示在按钮文本的实现及错误解决
解决SwiftUI异步数据加载的闭包捕获错误与数据展示问题
首先,你碰到的Escaping closure captures mutating 'self' parameter错误,核心原因是SwiftUI的View是值类型,在init方法里self处于可变(mutating)状态,而逃逸闭包不能捕获这种可变的self。另外你的代码还有几个细节问题需要修正,比如网络请求的completion没有正确调用、使用NS类型不够Swift化等。下面是完整的解决方案:
1. 重构GetLocations类
先把Objective-C风格的NSArray/NSDictionary换成Swift原生类型,同时确保异步请求完成后正确调用completion返回数据:
import Foundation class GetLocations: ObservableObject { // 用Swift原生数组替代NSArray,类型更安全 @Published var locations: [[String: Any]] = [] func fetchLocationNames(action: String, id: String, completion: @escaping ([[String: Any]]) -> Void) { // 先构造你的请求(这里需要补充实际的服务器接口地址和请求参数) guard let url = URL(string: "你的服务器接口地址") else { print("无效的URL") completion([]) return } var request = URLRequest(url: url) // 补充请求方法、参数等(示例为POST请求) request.httpMethod = "POST" let params = ["Action": action, "Id": id] request.httpBody = try? JSONSerialization.data(withJSONObject: params) request.setValue("application/json", forHTTPHeaderField: "Content-Type") let session = URLSession.shared session.dataTask(with: request) { [weak self] data, response, error in if let error = error { print("请求错误: \(error)") completion([]) return } guard let data = data else { print("未获取到数据") completion([]) return } do { if let parseResult = try JSONSerialization.jsonObject(with: data) as? [String: Any], let infoList = parseResult["InfoList"] as? [[String: Any]] { // 回到主线程更新UI相关数据(必须在主线程操作) DispatchQueue.main.async { self?.locations = infoList } completion(infoList) } else { print("数据格式无效") completion([]) } } catch { print("JSON解析错误: \(error)") completion([]) } }.resume() } }
2. 修改SwiftUI视图实现
用@ObservedObject监听数据变化替代@State,并在onAppear中触发数据请求(而非init),同时安全获取数组第一个元素的Name值:
import SwiftUI struct GetLocationData: View { // 用ObservedObject监听GetLocations的Published属性变化,自动刷新视图 @ObservedObject private var locationFetcher = GetLocations() var body: some View { VStack(spacing: 20) { Button(action: {}) { // 安全获取第一个元素的Name,无数据时显示默认文本 Text(locationFetcher.locations.first?["Name"] as? String ?? "Select Menu") .frame(minWidth: 0, maxWidth: 500) .padding() .background(Color.clear) .foregroundColor(.black) .font(.custom("Open Sans", size: 18)) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(Color.gray, lineWidth: 2) ) } } // 视图出现时触发数据请求,此时self已初始化完成,避免闭包捕获问题 .onAppear { locationFetcher.fetchLocationNames( action: "US", id: "ba6fcd92-3d1e-4fb6-b135-0c47ea1815cd" ) { locations in // 可选:在这里处理completion返回的额外逻辑 print("成功获取\(locations.count)条位置数据") } } } }
关键修改点说明
- 闭包捕获错误解决:用
onAppear替代init触发异步请求,此时self已经初始化完成,不再是mutating状态,彻底避免了逃逸闭包捕获mutating self的问题。 - 类型安全优化:把
NSArray换成Swift原生的[[String: Any]],减少强制类型转换的风险,代码更符合Swift的类型安全特性。 - UI自动刷新:通过
@ObservedObject和@Published的组合,当locations数组更新时,视图会自动刷新,按钮文本也会同步更新。 - 主线程更新:网络请求的回调在后台线程,更新
@Published属性时切换到主线程,确保UI操作的安全性。
内容的提问来源于stack exchange,提问作者Honey
相关产品推荐
相关产品推荐

