json - swiftUI / Xcode 12 从服务器获取数据
问题描述
我对 swiftUI 非常陌生,并且一直在学习景观应用教程。我一直在尝试将数据源从捆绑的 JSON 文件切换到远程 JSON 源,但到目前为止,我对如何将我所学到的关于 URLSession 的知识与教程加载代码相结合感到迷茫。
苹果的代码:
final class ModelData: ObservableObject {
@Published var landmarks: [Landmark] = load("landmarkData.json")
// @Published var landmarks: [Landmark] = apiCall.getLocations(locations)
}
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
我必须从远程源加载:
struct Location: Codable, Identifiable {
let id = UUID()
let country: String
let name: String
}
class apiCall {
func getLocations(completion:@escaping ([Location]) -> ()) {
guard let url = URL(string: "https://overseer.cyou/heritage/heritageData.json") else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
let locations = try! JSONDecoder().decode([Location].self, from: data!)
print(locations)
DispatchQueue.main.async {
completion(locations)
}
}
.resume()
}
}
任何人都可以告诉我我是如何做到这一点的,最好是从一个完整的初学者的角度来看?
解决方案
// framework support
import SwiftUI
import Combine
// List view setup
struct LocationsView: View {
@ObservedObject var viewModel = LocationModel()
var body: some View {
List(viewModel.locations) { location in
HStack {
VStack(alignment: .leading) {
Text(location.name)
.font(.headline)
Text(location.country)
.font(.subheadline)
}
}
}
}
}
// Location model
struct Location: Codable, Identifiable {
var id = UUID()
let country: String
let name: String
let locationId: Int = 0
enum CodingKeys: String, CodingKey {
case locationId = "id"
case country
case name
}
}
// Location view model class
class LocationModel: ObservableObject {
@Published var locations: [Location] = []
var cancellationToken: AnyCancellable?
init() {
getLocations()
}
}
extension LocationModel {
func getLocations() {
cancellationToken = self.request("https://overseer.cyou/heritage/heritageData.json")?
.mapError({ (error) -> Error in
print(error)
return error
})
.sink(receiveCompletion: { _ in },
receiveValue: {
self.locations = $0
})
}
// API request
private func request(_ path: String) -> AnyPublisher<[Location], Error>? {
guard let url = URL(string: path)
else { return nil }
let request = URLRequest(url: url)
return apiCall.run(request)
.map(\.value)
.eraseToAnyPublisher()
}
}
// API setup
struct apiCall {
struct Response<T> {
let value: T
let response: URLResponse
}
static func run<T: Decodable>(_ request: URLRequest) -> AnyPublisher<Response<T>, Error> {
return URLSession.shared
.dataTaskPublisher(for: request)
.tryMap { result -> Response<T> in
let value = try JSONDecoder().decode(T.self, from: result.data)
return Response(value: value, response: result.response)
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
推荐阅读
- python - scipy峰检测中的“宽度”是什么意思
- r - 在一列中使用 cumsum() 迭代
- r - 在 R 中调用具有数据集和响应变量的函数时出错
- r - “在验证窗口 1 中返回模型 1 的类 'try-error'”
- python - BeautifulSoup Amazon Web Scraping - NoneType 错误
- javascript - 如何在 Discord.js 中使用超时
- python-3.x - 在pythonista中添加小数时如何防止计算错误?
- testing - 手风琴未通过可访问性检查
- react-native - 如何构建完全在智能合约上运行的移动应用程序?
- java - JavaFx 将输入控件标记为强制