SwiftUI调用API时出现Unexpected non-void return value错误如何解决?
问题原因
- 你调用的
URLSession.shared.dataTask是异步执行的网络任务,它的尾随闭包返回类型为Void,你在闭包里写return img.string相当于尝试给这个无返回值的闭包返回字符串,自然会报"Unexpected non-void return value in void function"错误 - 其次异步任务不会阻塞当前函数执行,
getFavoriteDoggo在调用.resume()之后就会直接执行结束,根本等不到网络请求返回结果,所以不可能通过同步return的方式拿到URL字符串
解决方案
你确实需要用completion handler来接收异步返回的结果,实现步骤如下:
- 改造
getFavoriteDoggo函数,移除同步返回值,新增completion回调参数 - 网络请求拿到结果后,在主队列调用completion把URL字符串回传
- 改造各个犬种对应的获取方法,在completion回调中给对应的
@Published属性赋值,不需要再返回值
改造后的完整代码
import Combine import SwiftyJSON // 保留你原有项目的SwiftyJSON引入 class DoggoViewModel: ObservableObject { @Published var mamaluteImg = "" @Published var goldenImg = "" @Published var samoyedImg = "" @Published var chowImg = "" @Published var huskyImg = "" func getMamalute() { getFavoriteDoggo(breed: "malamute") { [weak self] imgUrl in guard let self = self, let imgUrl = imgUrl else { return } self.mamaluteImg = imgUrl } } func getChowChow() { getFavoriteDoggo(breed: "chow") { [weak self] imgUrl in guard let self = self, let imgUrl = imgUrl else { return } self.chowImg = imgUrl } } func getHusky() { getFavoriteDoggo(breed: "husky") { [weak self] imgUrl in guard let self = self, let imgUrl = imgUrl else { return } self.huskyImg = imgUrl } } func getSamoyed() { getFavoriteDoggo(breed: "samoyed") { [weak self] imgUrl in guard let self = self, let imgUrl = imgUrl else { return } self.samoyedImg = imgUrl } } func getGoldenRetriever() { getFavoriteDoggo(breed: "retriever/golden") { [weak self] imgUrl in guard let self = self, let imgUrl = imgUrl else { return } self.goldenImg = imgUrl } } // 新增completion回调,参数为可选的URL字符串 func getFavoriteDoggo(breed: String, completion: @escaping (String?) -> Void) { guard let url = URL(string: "https://dog.ceo/api/breed/\(breed)/images/random") else { print("Trouble parsing url") completion(nil) return } let request = URLRequest(url: url) URLSession.shared.dataTask(with: request){ data, response, error in if let error = error { print(error.localizedDescription) completion(nil) return } guard let data = data else { completion(nil) return } do { let response = try JSON(data: data) let imgUrl = response["message"].string // 切换主队列回调,保证UI更新在主线程执行 DispatchQueue.main.async { completion(imgUrl) } } catch { print("JSON解析失败: \(error.localizedDescription)") DispatchQueue.main.async { completion(nil) } } }.resume() } }
补充说明
如果你的项目最低支持iOS 15/macOS 12及以上版本,也可以用Swift Concurrency的async/await写法,代码会更简洁,不需要嵌套completion回调。
内容的提问来源于stack exchange,提问作者Maanas
相关产品推荐
相关产品推荐

