首页 > 解决方案 > SwiftUI:如何让服务器数据显示?

问题描述

场景:从服务器访问数据(可变大小)并显示它。在这种情况下,一系列国家。

在进入此视图(或之前)时,应调用一次 (1)服务器访问。

观察:我可以通过发布者中的调试器和打印语句查看数据。

问题:数据不显示在视图中。

以下是整个代码(简化为通过游乐场对一系列国家/地区的简单请求)。

请随意剪切/粘贴到操场上。
它应该按预期运行,但不显示任何数据。

解决方案?

import Combine
import SwiftUI
import PlaygroundSupport

// ---------------------------------------------------------------------------

var cancellables: Set<AnyCancellable> = []

struct VaccinesDataView: View {
    @State private var appleData: [String]?
    @StateObject var appleCountries = AppleCountries()

    var body: some View {
        Form {
            Section(header: VStack(alignment: .leading) {
                Text("Apple Data")
            }) {
                if let vacData = appleData {
                    ForEach(vacData, id: \.self) { source in

                        HStack {
                            Text(source)
                                .font(.title)
                        }
                    }
                }
            }
            .navigationBarTitle("Vaccines", displayMode: .inline)
        }.onAppear {
            self.appleCountries.getData()
        }

        .navigationViewStyle(StackNavigationViewStyle())
    }
}

// =====================================================================================================

class AppleCountries: ObservableObject {
    @Published var appleData: [String]?

    func getData() {
        let appleURL = URL(string: "https://disease.sh/v3/covid-19/apple/countries")!
        let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: appleURL)
            .map(\.data)
            .receive(on: DispatchQueue.main)
            .decode(type: [String].self, decoder: JSONDecoder())
            .print("Apples: ")

        remoteDataPublisher
            .eraseToAnyPublisher()
            .sink(receiveCompletion: { completion in
                switch completion {
                case .finished:
                    print("Publisher Finished")
                case let .failure(anError):
                    Swift.print("\nReceived error: ", anError)
                }
            }, receiveValue: { someValue in
                self.appleData = someValue
                print("appleData: \(String(describing: self.appleData))")
            }).store(in: &cancellables)
    }
}

PlaygroundPage.current.setLiveView(VaccinesDataView())

提示:当我只想要一 (1) 个时,我注意到对服务器的重复访问。

服务器访问是通过.onAppear ()可能被多次调用的。

Apples: : receive subscription: (Decode)
Apples: : request unlimited
Apples: : receive subscription: (Decode)
Apples: : request unlimited
Apples: : receive subscription: (Decode)
Apples: : request unlimited
Apples: : receive subscription: (Decode)
Apples: : request unlimited
Apples: : receive value: (["Albania", "Argentina", "Australia", "Austria", "Belgium", "Brazil", "Bulgaria", "Cambodia", "Canada", "Chile", "Colombia", "Croatia", "Czechia", "Denmark", "Egypt", "Estonia", "Finland", "France", "Germany", "Greece", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Ireland", "Israel", "Italy", "Japan", "Latvia", "Lithuania", "Luxembourg", "Macao", "Malaysia", "Mexico", "Morocco", "Netherlands", "New Zealand", "Norway", "Philippines", "Poland", "Portugal", "S. Korea", "Romania", "Russia", "Saudi Arabia", "Serbia", "Singapore", "Slovakia", "Slovenia", "South Africa", "Spain", "Sweden", "Switzerland", "Taiwan", "Thailand", "Turkey", "Ukraine", "UAE", "UK", "USA", "Uruguay", "Vietnam"])
appleData: Optional(["Albania", "Argentina", "Australia", "Austria", "Belgium", "Brazil", "Bulgaria", "Cambodia", "Canada", "Chile", "Colombia", "Croatia", "Czechia", "Denmark", "Egypt", "Estonia", "Finland", "France", "Germany", "Greece", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Ireland", "Israel", "Italy", "Japan", "Latvia", "Lithuania", "Luxembourg", "Macao", "Malaysia", "Mexico", "Morocco", "Netherlands", "New Zealand", "Norway", "Philippines", "Poland", "Portugal", "S. Korea", "Romania", "Russia", "Saudi Arabia", "Serbia", "Singapore", "Slovakia", "Slovenia", "South Africa", "Spain", "Sweden", "Switzerland", "Taiwan", "Thailand", "Turkey", "Ukraine", "UAE", "UK", "USA", "Uruguay", "Vietnam"])
Apples: : receive finished
Publisher Finished
Apples: : receive value: (["Albania", "Argentina", "Australia", "Austria", "Belgium", "Brazil", "Bulgaria", "Cambodia", "Canada", "Chile", "Colombia", "Croatia", "Czechia", "Denmark", "Egypt", "Estonia", "Finland", "France", "Germany", "Greece", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Ireland", "Israel", "Italy", "Japan", "Latvia", "Lithuania", "Luxembourg", "Macao", "Malaysia", "Mexico", "Morocco", "Netherlands", "New Zealand", "Norway", "Philippines", "Poland", "Portugal", "S. Korea", "Romania", "Russia", "Saudi Arabia", "Serbia", "Singapore", "Slovakia", "Slovenia", "South Africa", "Spain", "Sweden", "Switzerland", "Taiwan", "Thailand", "Turkey", "Ukraine", "UAE", "UK", "USA", "Uruguay", "Vietnam"])
appleData: Optional(["Albania", "Argentina", "Australia", "Austria", "Belgium", "Brazil", "Bulgaria", "Cambodia", "Canada", "Chile", "Colombia", "Croatia", "Czechia", "Denmark", "Egypt", "Estonia", "Finland", "France", "Germany", "Greece", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Ireland", "Israel", "Italy", "Japan", "Latvia", "Lithuania", "Luxembourg", "Macao", "Malaysia", "Mexico", "Morocco", "Netherlands", "New Zealand", "Norway", "Philippines", "Poland", "Portugal", "S. Korea", "Romania", "Russia", "Saudi Arabia", "Serbia", "Singapore", "Slovakia", "Slovenia", "South Africa", "Spain", "Sweden", "Switzerland", "Taiwan", "Thailand", "Turkey", "Ukraine", "UAE", "UK", "USA", "Uruguay", "Vietnam"])
Apples: : receive finished
Publisher Finished
...
...
...

标签: swiftuicombine

解决方案


您不需要额外的状态appleData- 它没有填充 -appleCountries直接使用如下。

使用 Xcode 12.1 / iOS 14.1 测试

演示

struct VaccinesDataView: View {
    @StateObject var appleCountries = AppleCountries()

    var body: some View {
        Form {
            Section(header: VStack(alignment: .leading) {
                Text("Apple Data")
            }) {
                if let vacData = appleCountries.appleData {    //  !!
                    ForEach(vacData, id: \.self) { source in

                        HStack {
                            Text(source)
                                .font(.title)
                        }.onAppear {
                            appleCountries.getData()
                        }
                    }
                }
            }
            .navigationBarTitle("Vaccines", displayMode: .inline)
        }.onAppear {
            self.appleCountries.getData()
        }

        .navigationViewStyle(StackNavigationViewStyle())
    }
}

推荐阅读