使用模型的通用值(相对于静态 model.self)使用 JSONDecoder 进行解析
Using a generic value for the model (vs a static model.self) to be parsed with JSONDecoder
场景:我想使用一个通用函数来访问各种端点。所以我正在尝试创建样板代码来处理各种数据模型 (struct.self) 及其关联的 URLs.
这里有几个模型共享一个共同点 URL会话 + 解码代码:
// MARK: - Region
struct Region: Codable {
let country: String
let subregions: [String]
}
// MARK: - SubRegion
struct SubRegion: Codable {
let country: String
let subregion: Sub
let data: [Datum]
}
这是一个数据向量 (Region
),其中包含 URL 及其关联的数据模型:
struct URLDataModel {
var url = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
var dataModel: Any = Region.self
}
以下是包含使用 URLDataModel
结构的共享 getRegionList()
的整个 class:
class CountryRegionListModelView: ObservableObject {
@Published var countryRegionList = [String]()
// Data Persistence:
var cancellables: Set<AnyCancellable> = []
// TODO: --- Get Region tag ---
func getRegionList(urlDataModel: URLDataModel) {
// MARK: - Region
struct Country: Codable {
let countries: [String]
}
struct Region: Codable {
let country: String
let subregions: [String]
}
print("url: ", urlDataModel.url)
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: urlDataModel.url!)
// the dataTaskPublisher output combination is (data: Data, response: URLResponse)
.map(\.data)
.receive(on: DispatchQueue.main)
.print("Hello Data")
.decode(type: urlDataModel.dataModel, decoder: JSONDecoder())
remoteDataPublisher
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
break
case let .failure(anError):
Swift.print("received error: ", anError)
}
}, receiveValue: { someValue in
self.countryRegionList = someValue.subregions
print(self.countryRegionList)
}).store(in: &cancellables)
}
}
问题:我不知道如何让JSON解码器与变量模型类型一起工作'model'.self.
我应该使用泛型吗?
那将如何工作?
您可以创建协议:
protocol URLResource {
associatedtype DataModel: Decodable
var url: URL? { get }
}
及其示例实现:
struct CovidResource: URLResource {
typealias DataModel = Region
var url = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
}
然后您可以使 getRegionList
通用并接受 some URLResource:
func getRegionList<Resource>(urlDataModel: Resource) where Resource: URLResource {
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: resource.url!)
// the dataTaskPublisher output combination is (data: Data, response: URLResponse)
.map(\.data)
.receive(on: DispatchQueue.main)
.decode(type: Resource.DataModel.self, decoder: JSONDecoder())
// ...
}
我终于成功了;通过故事板:
import Combine
import SwiftUI
protocol URLResource {
associatedtype DataModel: Decodable
var url: URL? { get }
}
var cancellables: Set<AnyCancellable> = []
struct CovidResource<T:Decodable>: URLResource {
typealias DataModel = T
var url = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
}
// MARK: - Canada
struct Canada: Codable {
let country: String
let subregions: [String]
}
let myURL = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")!
func getRegionList<Resource>(urlDataModel: Resource) where Resource: URLResource {
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: urlDataModel.url!)
.map(\.data)
.receive(on: DispatchQueue.main)
.decode(type: Resource.DataModel.self, decoder: JSONDecoder())
.print("Remote Publisher \(urlDataModel.url!): ")
remoteDataPublisher
.sink(receiveCompletion: { completion in
print(completion)
switch completion {
case .finished:
print("Publisher Finished")
break
case let .failure(anError):
Swift.print("received error: ", anError)
}
}, receiveValue: { someValue in
print("someValue: ", someValue)
}).store(in: &cancellables)
}
// ---------------------------------------
let resource = CovidResource<Canada>(url: myURL)
getRegionList(urlDataModel: resource)
print("End of Proof-of-Concept")
print("The End.")
控制台输出:
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : receive subscription: (Decode)
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : request unlimited
End of Proof-of-Concept
The End.
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : receive value: (Canada(country: "Canada", subregions: ["Alberta", "Calgary", "Edmonton", "British Columbia", "Vancouver", "Manitoba", "New Brunswick", "Newfoundland and Labrador", "Northwest Territories", "Halifax", "Nova Scotia", "Ontario", "Ottawa", "Toronto", "Prince Edward Island", "Montreal", "Quebec", "Saskatchewan", "All", "Yukon Territory"]))
someValue: Canada(country: "Canada", subregions: ["Alberta", "Calgary", "Edmonton", "British Columbia", "Vancouver", "Manitoba", "New Brunswick", "Newfoundland and Labrador", "Northwest Territories", "Halifax", "Nova Scotia", "Ontario", "Ottawa", "Toronto", "Prince Edward Island", "Montreal", "Quebec", "Saskatchewan", "All", "Yukon Territory"])
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : receive finished
finished
Publisher Finished
场景:我想使用一个通用函数来访问各种端点。所以我正在尝试创建样板代码来处理各种数据模型 (struct.self) 及其关联的 URLs.
这里有几个模型共享一个共同点 URL会话 + 解码代码:
// MARK: - Region
struct Region: Codable {
let country: String
let subregions: [String]
}
// MARK: - SubRegion
struct SubRegion: Codable {
let country: String
let subregion: Sub
let data: [Datum]
}
这是一个数据向量 (Region
),其中包含 URL 及其关联的数据模型:
struct URLDataModel {
var url = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
var dataModel: Any = Region.self
}
以下是包含使用 URLDataModel
结构的共享 getRegionList()
的整个 class:
class CountryRegionListModelView: ObservableObject {
@Published var countryRegionList = [String]()
// Data Persistence:
var cancellables: Set<AnyCancellable> = []
// TODO: --- Get Region tag ---
func getRegionList(urlDataModel: URLDataModel) {
// MARK: - Region
struct Country: Codable {
let countries: [String]
}
struct Region: Codable {
let country: String
let subregions: [String]
}
print("url: ", urlDataModel.url)
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: urlDataModel.url!)
// the dataTaskPublisher output combination is (data: Data, response: URLResponse)
.map(\.data)
.receive(on: DispatchQueue.main)
.print("Hello Data")
.decode(type: urlDataModel.dataModel, decoder: JSONDecoder())
remoteDataPublisher
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
break
case let .failure(anError):
Swift.print("received error: ", anError)
}
}, receiveValue: { someValue in
self.countryRegionList = someValue.subregions
print(self.countryRegionList)
}).store(in: &cancellables)
}
}
问题:我不知道如何让JSON解码器与变量模型类型一起工作'model'.self.
我应该使用泛型吗?
那将如何工作?
您可以创建协议:
protocol URLResource {
associatedtype DataModel: Decodable
var url: URL? { get }
}
及其示例实现:
struct CovidResource: URLResource {
typealias DataModel = Region
var url = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
}
然后您可以使 getRegionList
通用并接受 some URLResource:
func getRegionList<Resource>(urlDataModel: Resource) where Resource: URLResource {
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: resource.url!)
// the dataTaskPublisher output combination is (data: Data, response: URLResponse)
.map(\.data)
.receive(on: DispatchQueue.main)
.decode(type: Resource.DataModel.self, decoder: JSONDecoder())
// ...
}
我终于成功了;通过故事板:
import Combine
import SwiftUI
protocol URLResource {
associatedtype DataModel: Decodable
var url: URL? { get }
}
var cancellables: Set<AnyCancellable> = []
struct CovidResource<T:Decodable>: URLResource {
typealias DataModel = T
var url = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")
}
// MARK: - Canada
struct Canada: Codable {
let country: String
let subregions: [String]
}
let myURL = URL(string: "https://disease.sh/v3/covid-19/apple/countries/Canada")!
func getRegionList<Resource>(urlDataModel: Resource) where Resource: URLResource {
let remoteDataPublisher = URLSession.shared.dataTaskPublisher(for: urlDataModel.url!)
.map(\.data)
.receive(on: DispatchQueue.main)
.decode(type: Resource.DataModel.self, decoder: JSONDecoder())
.print("Remote Publisher \(urlDataModel.url!): ")
remoteDataPublisher
.sink(receiveCompletion: { completion in
print(completion)
switch completion {
case .finished:
print("Publisher Finished")
break
case let .failure(anError):
Swift.print("received error: ", anError)
}
}, receiveValue: { someValue in
print("someValue: ", someValue)
}).store(in: &cancellables)
}
// ---------------------------------------
let resource = CovidResource<Canada>(url: myURL)
getRegionList(urlDataModel: resource)
print("End of Proof-of-Concept")
print("The End.")
控制台输出:
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : receive subscription: (Decode)
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : request unlimited
End of Proof-of-Concept
The End.
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : receive value: (Canada(country: "Canada", subregions: ["Alberta", "Calgary", "Edmonton", "British Columbia", "Vancouver", "Manitoba", "New Brunswick", "Newfoundland and Labrador", "Northwest Territories", "Halifax", "Nova Scotia", "Ontario", "Ottawa", "Toronto", "Prince Edward Island", "Montreal", "Quebec", "Saskatchewan", "All", "Yukon Territory"]))
someValue: Canada(country: "Canada", subregions: ["Alberta", "Calgary", "Edmonton", "British Columbia", "Vancouver", "Manitoba", "New Brunswick", "Newfoundland and Labrador", "Northwest Territories", "Halifax", "Nova Scotia", "Ontario", "Ottawa", "Toronto", "Prince Edward Island", "Montreal", "Quebec", "Saskatchewan", "All", "Yukon Territory"])
Remote Publisher https://disease.sh/v3/covid-19/apple/countries/Canada: : receive finished
finished
Publisher Finished