'Unexpected non-void return value in void function' 在 swiftui 中

'Unexpected non-void return value in void function' in swiftui

基本上,我在 API 调用中 return 以字符串形式输入 url 时遇到问题。

我正在尝试通过一个采用 'breed' 参数调用其端点的大函数来实现抽象。这样我就避免了多次编写完全相同的函数。函数 getMamalute()、getGolden() 等都传入此参数以获取 URL,以便我可以将其作为图像显示在我的视图中——您可能已经知道了。但是我在 'getFavoriteDoggo' 函数的 return 行收到以下错误 'Unexpected non-void return value in void function'。我需要使用完成处理程序吗?如果是这样,那会是什么样子?

    @Published var mamaluteImg = ""
    @Published var goldenImg = ""
    @Published var samoyedImg = ""
    @Published var chowImg = ""
    @Published var huskyImg = ""

    func getMamalute() -> String{
        return getFavoriteDoggo(breed: "malamute")
    }
    
    func getChowChow() -> String{
        return getFavoriteDoggo(breed: "chow")
    }
    
    func getHusky() -> String{
        return getFavoriteDoggo(breed: "husky")
    }
    
    func getSamoyed() -> String{
        return getFavoriteDoggo(breed: "samoyed")
    }
    
    func getGoldenRetriever() -> String{
        return getFavoriteDoggo(breed: "retriever/golden")
    }

func getFavoriteDoggo(breed: String) -> String{
        
        guard let url = URL(string: "https://dog.ceo/api/breed/\(breed)/images/random") else {
            print("Trouble parsing url")
            return ""
        }
        
        let request = URLRequest(url: url)
        
        URLSession.shared.dataTask(with: request){ data, response, error in
            if error != nil {
                print((error?.localizedDescription)!)
                return
            }
            
            if let data = data {
                let response = try! JSON(data: data)
//                let randoIndex = Int.random(in: 0...(response.count - 1))
                let img = response["message"]
                
//                print(img)
                
//                DispatchQueue.main.async {
//                    self.mamaluteImg = img.string!
//                }
                return img.string
            }
        }.resume()
    }

希望我清楚地解释了我的问题,如果不是我的道歉,我的大脑 运行 电池电量真的很低,所以我很乐意在下面帮助澄清:)

再次感谢!

您正在使用异步方法 (dataTask)。你不知道什么时候完成运行(网络请求)。因此它不能有 return 值。完成后执行闭包 (URLSession.shared.dataTask (with: request) {// this block}).

你肯定会喜欢这样:

class DogManager {
    var imageInfos: String?
    
    func getFavoriteDoggo(breed: String) {
        guard let url = URL(string: "https://dog.ceo/api/breed/\(breed)/images/random") else {
            print("Trouble parsing url")
            return
        }

        URLSession.shared.dataTask(with: url) { data, response, error in
            guard error == nil, (response as? HTTPURLResponse)?.statusCode == 200 else {
                return
            }
            if let data = data {
                self.imageInfos = String(data: data, encoding: .utf8)
                print(self.imageInfos ?? "no infos")
            }
        }.resume()
    }
}

let manager = DogManager()
manager.getFavoriteDoggo(breed: "retriever/golden")

您可以在 Playground 中进行测试。

现在,如果您想使用 SwiftUI 并且在 imageInfos 更改时重绘您的视图,您必须将 class 更改为 ObservableObject:

class DogManager: ObservableObject {
    @Published var imageInfos: String?
    //....//
}

并像这样使用它:

struct MainView: View {
    @StateObject private var dm = DogManager()

    var body: some View {
        Text(dm.imageInfos ?? "nothing")
            .onAppear {
                dm.getFavoriteDoggo(breed: "retriever/golden")
            }
    }
}

iOS15 :

请注意,随着 async / await (iOS15) 的引入,您可以编写具有 return 值的异步方法(就像您所做的那样):

    @available(iOS 15.0, *)
    func getFavoriteDoggo(breed: String) async -> String? {
        guard let url = URL(string: "https://dog.ceo/api/breed/\(breed)/images/random"),
              let (data, response) = try? await URLSession.shared.data(from: url),
              (response as? HTTPURLResponse)?.statusCode == 200 else { return nil }
        return String(data: data, encoding: .utf8)
    }

您可以将它与新的 .task 修饰符一起使用:

struct MainView: View {
    var dm = DogManager()
    @State private var imageInfos: String?
    var body: some View {
        Text(imageInfos ?? "nothing")
            .task {
                await imageInfos = dm.getFavoriteDoggo(breed: "retriever/golden")
            }
    }
}

编辑:

"Hey thank you for helping me out, but this would only work for only 1 dog breed."

首先,让我们创建一个新的 Dog 结构。 A Dog 有一个品种及其图像上的信息,最初不存在 (nil)。

struct Dog: Identifiable {
    let id = UUID()
    let breed: String
    var imageInfos: String?

    init(_ breed: String) {
        self.breed = breed
    }
}

我们的视图将显示一组狗:

@State private var dogs: [Dog] = ["malamute", "chow", "husky", "samoyed"].map(Dog.init)

现在我们更改获取狗图像的函数:它接受一个 Dog 作为参数,并且 returns(当它完成时)一个 Dog ( imageInfos 填充):

func updateImageOf(dog: Dog) async -> Dog {
        var newDog = dog
        guard let url = URL(string: "https://dog.ceo/api/breed/\(dog.breed)/images/random"),
              let (data, response) = try? await URLSession.shared.data(from: url),
              (response as? HTTPURLResponse)?.statusCode == 200 else { return dog }
        newDog.imageInfos = String(data: data, encoding: .utf8)
        return newDog
    }

我们创建了第二个函数,它对几只狗执行相同的操作。

func updateImagesOf(favoriteDogs: [Dog]) async -> [Dog] {
        var results: [Dog] = []
        await withTaskGroup(of: Dog.self) { group in
            for dog in favoriteDogs {
                group.async {
                    await self.updateImageOf(dog: dog)
                }
            }
            for await result in group {
                results.append(result)
            }
        }
        return results
    }

我们在 View:

中使用了这个函数
struct MainView: View {
    var dm = DogManager()

    @State private var dogs: [Dog] = ["malamute", "chow", "husky", "samoyed"].map(Dog.init)

    var body: some View {
        List(dogs) { dog in
            HStack {
                Text(dog.breed)
                    .padding(.trailing, 40)
                Text(dog.imageInfos ?? "rien")
            }
        }
        .task {
            await dogs = dm.updateImagesOf(favoriteDogs: dogs)
        }
    }
}

有效(模拟器,Xcode 13 beta2)