Return Children and Siblings values in response - fluent

It's probably a trivial question, but I couldn't solve it and didn't find the answer.
I have 3 tables related to each other as Parent-Child and Siblings. And I want to return some values form all of the tables and the same JSON response.
final class ClimbingGym: Model, Content {
static let schema = "climbing_gyms"
#ID(key: .id)
var id: UUID?
#Field(key: "name")
var name: String
#Field(key: "description")
var description: String
#Siblings(through: ClimbingDisciplineClimbingGym.self, from: \.$gym, to: \.$discipline)
var disciplines: [ClimbingDiscipline]
#Children(for: \.$gym)
var socialNetworks: [SocialNetwork]
init() { }
init(
id: UUID? = nil,
name: String,
description: String,
) {
self.id = id
self.name = name
self.description = description
}
}
enum ClimbingDisciplineType: String, Codable, CaseIterable, Equatable {
static let schema = "climbing_discipline_type"
case lead
case boulder
case speed
}
final class ClimbingDiscipline: Model, Content {
static let schema = "climbing_disciplines"
#ID(key: .id)
var id: UUID?
#Enum(key: "type")
var type: ClimbingDisciplineType
init() { }
init(
id: UUID? = nil,
type: ClimbingDisciplineType
) {
self.id = id
self.type = type
}
}
final class SocialNetwork {
static let schema = "social_networks"
#ID(key: .id)
var id: UUID?
#Field(key: "link")
var link: String
#Parent(key: "gym_id")
var gym: ClimbingGym
init() { }
init(
id: UUID? = nil,
link: String,
gym: ClimbingGym
) throws {
self.id = id
self.link = link
self.$gym.id = try gym.requireID()
}
}
And I want to return that model:
struct ClimbingGymResponse: Codable, Content {
let id: UUID
let name: String
let description: String
let disciplines: [ClimbingDisciplineType]
let socialNetworks: [String]
}
so the query that I'm using now looks like that
func getAll(req: Request) throws -> EventLoopFuture<[ClimbingGymResponse]> {
ClimbingGym
.query(on: req.db)
.join(children: \ClimbingGym.$socialNetworks)
.join(siblings: \ClimbingGym.$disciplines)
.all()
}
and it obviously doesn't work, because it returns [ClimbingGym] instead of [ClimbingGymResponse].
So how can I transform one to another?
I have problems with filling disciplines: [ClimbingDisciplineType] and socialNetworks: [String] field for each gym
Thank you!

You can map the array of results into the type you want. So
ClimbingGym
.query(on: req.db)
.with(\.$socialNetworks)
.with(\.$disciplines)
.all().flatMapThrowing { gyms in
try gyms.map { try ClimbingGymResponse(id: $0.requireID(), ...) }
}

Related

How convert data to Json in Realm version 10.15.0

in the realm version 10.7.1 it used this code but when migrating to 10.15.0 it is no longer possible because the ListBase class no longer exists
extension Object {
func toWebServiceRequest() -> [String:AnyObject] {
let properties = self.objectSchema.properties.map { $0.name }
var dicProps = [String:AnyObject]()
for (key, value) in self.dictionaryWithValues(forKeys: properties) {
if let value = value as? ListBase {
dicProps[key] = value.toArray() as AnyObject
} else if let value = value as? Object {
dicProps[key] = value.toWebServiceRequest() as AnyObject
} else {
dicProps[key] = value as AnyObject
}
}
return dicProps
}
}
extension ListBase {
func toArray() -> [AnyObject] {
var _toArray = [AnyObject]()
for i in 0..<self._rlmArray.count {
let obj = unsafeBitCast(self._rlmArray[i], to: Object.self)
_toArray.append(obj.toWebServiceRequest() as AnyObject)
}
return _toArray
}
}
The mongo team wants you to use the Codable protocol rather than some custom encoding mechanism. This is really the proper way to do it, otherwise you have to rely on Realm implementation details.
class Parent: Object, Codable {
#Persisted var name: String = ""
#Persisted var children: List<Child>
convenience init(name: String, children: [Child] = []) {
self.init()
self.name = name
self.children.append(objectsIn: children)
}
}
If the server is expecting keys different from your object's property names you can define custom CodingKeys:
class Child: Object, Codable {
#Persisted var id: Int = 0
#Persisted var name: String = ""
enum CodingKeys: String, CodingKey {
case id = "_id"
case name = "name"
}
convenience init(id: Int, name: String) {
self.init()
self.id = id
self.name = name
}
}
Usage is simple:
func makeParent() -> Parent {
Parent(name: "Alex", children: [
Child(id: 1, name: "Jim"),
Child(id: 2, name: "Tom"),
Child(id: 3, name: "Sam"),
Child(id: 4, name: "Joe"),
])
}
func encode<Item: Encodable>(_ item: Item) throws -> Data {
let encoder = JSONEncoder()
return try encoder.encode(item)
}
func convertDataToString(_ data: Data) throws -> String? {
return String(data: data, encoding: .utf8)
}
func decode<Item: Decodable>(_ type: Item.Type, from data: Data) throws -> Item {
let decoder = JSONDecoder()
return try decoder.decode(type, from: data)
}
func executeProgram() throws {
let parent = makeParent()
let encodedJSONData = try encode(parent)
guard let encodedJSONString = try convertDataToString(encodedJSONData) else {
throw Error.badJSON
}
print("JSON: \(encodedJSONString)")
let decodedParent = try decode(Parent.self, from: encodedJSONData)
print("Decoded Parent: \(decodedParent)")
}
On the encode route this results in:
JSON:
{"name":"Alex","children":[{"_id":1,"name":"Jim"},{"_id":2,"name":"Tom"},{"_id":3,"name":"Sam"},{"_id":4,"name":"Joe"}]}
And on the decode route this results in:
Decoded Parent: Parent { name = Alex; children = List
<0x600000a2b800> ( [0] Child { id = 1; name = Jim; }, [1]
Child { id = 2; name = Tom; }, [2] Child { id = 3;
name = Sam; }, [3] Child { id = 4; name = Joe; } ); }
You can obviously omit the String conversion just work with the resulting Data.

Parse JSON with swiftyJSON and more

Hi I've tried to parse my JSON but I couldn't get my data from it,
(I used SwiftyJSON)
how can I parse this ugly JSON?
//Mark: parser functions:
private func parseProvincesResult(provincesJSON: JSON, completion: #escaping(_ :ProvincesV2) -> Void) {
print(provincesJSON)
let errorCode: Int = provincesJSON["ErrorCode"].intValue
let errorDescriptions: String = provincesJSON["ErrorString"].stringValue
let newMacKey: String = provincesJSON["NewMacKey"].stringValue
let newPinKey: String = provincesJSON["NewPinKey"].stringValue
let version: Int = provincesJSON["Ver"].intValue
var provinceList: [ProvinceListResult] = []
for i in provincesJSON["ProvinceListResult"].arrayValue {
let id: Int = i["Id"].intValue
let name: String = i["Name"].stringValue
let proList = ProvinceListResult(id: id, name: name)
provinceList.append(proList)
}
let model = ProvincesV2(errorCode: errorCode, errorDescriptions: errorDescriptions, newMacKey: newMacKey, newPinKey: newPinKey, version: version, provinceList: provinceList)
completion(model)
}
and my JSON is:
{"ErrorCode":"8",
"ErrorString":"عملیات با موفقیت انجام شد.",
"NewMacKey":"vph+eLFgxa6LVq90QfsNUA==",
"NewPinKey":"evJiM9W6S9RWEClR6csxEQ==",
"Ver":201,
"ProvinceListResult":[{"Id":1,"Name":"آذربايجان شرقي"},
{"Id":2,"Name":"آذربايجان غربي"},
{"Id":3,"Name":"اردبيل"},
{"Id":4,"Name":"اصفهان"},
{"Id":5,"Name":"البرز"},
{"Id":6,"Name":"ايلام"},
{"Id":7,"Name":"بوشهر"},
{"Id":8,"Name":"تهران"},
{"Id":9,"Name":"چهارمحال و بختياري"},
{"Id":10,"Name":"خراسان جنوبي"},{"Id":11,"Name":"خراسان رضوي"},{"Id":12,"Name":"خراسان شمالي"},{"Id":13,"Name":"خوزستان"},{"Id":14,"Name":"زنجان"},{"Id":15,"Name":"سمنان"},{"Id":16,"Name":"سيستان و بلوچستان"},{"Id":17,"Name":"فارس"},{"Id":18,"Name":"قزوين"},{"Id":19,"Name":"قم"},{"Id":20,"Name":"کردستان"},{"Id":21,"Name":"کرمان"},{"Id":22,"Name":"کرمانشاه"},{"Id":23,"Name":"کهکيلويه و بويراحمد"},{"Id":24,"Name":"گلستان"},{"Id":25,"Name":"گيلان"},{"Id":26,"Name":"لرستان"},{"Id":27,"Name":"مازندران"},{"Id":28,"Name":"مرکزي"},{"Id":29,"Name":"هرمزگان"},{"Id":30,"Name":"همدان"},{"Id":31,"Name":"يزد"},{"Id":44,"Name":"کیش"}]}
how can I parse It?
tnx
Using Codable, you could do this:
import Foundation
// MARK: - Welcome
struct Welcome: Codable {
let errorCode, errorString, newMACKey, newPinKey: String
let ver: Int
let provinceListResult: [ProvinceListResult]
enum CodingKeys: String, CodingKey {
case errorCode = "ErrorCode"
case errorString = "ErrorString"
case newMACKey = "NewMacKey"
case newPinKey = "NewPinKey"
case ver = "Ver"
case provinceListResult = "ProvinceListResult"
}
}
// MARK: - ProvinceListResult
struct ProvinceListResult: Codable {
let id: Int
let name: String
enum CodingKeys: String, CodingKey {
case id = "Id"
case name = "Name"
}
}
(generated by https://app.quicktype.io)
And getting a value out might look like:
let welcome = try? JSONDecoder().decode(Welcome.self, from: jsonData)
print(welcome?.provinceListResult[1])
print(welcome?.provinceListResult[1])
If your original data is in String form, it can be converted to Data by doing this:
myStringJSON.data(using: .utf8)!
create two models for parse JSON
first for the province:
public struct ProvinceModel {
var id: Int?
var name: String?
init(json: JSON) {
self.id = json["Id"].int
self.name = json["Name"].string
}
}
with a constructor, you can parse JSON and set data to variables.
and second model:
public struct MyJSONModel {
var errorString: String?
var newMacKey: String?
var newPinKey: String?
var Ver: Int?
var Provinces: [ProvinceModel]?
init(json: JSON) {
// simple parse
self.errorString = json["ErrorCode"].string
self.newMacKey = json["NewMacKey"].string
self.newPinKey = json["NewPinKey"].string
self.Ver = json["Ver"].int
// array
self.Provinces = [] // empty array
if let jsonArray = json["ProvinceListResult"].array {
for json in jsonArray {
let ProvinceObject = ProvinceModel.init(json: json)
self.Provinces?.append(ProvinceObject)
}
}
}
}
for parse province items in the array, you must create a loop and create an object then set in variables
I prefer to use a constructor for parse JSON instead of a custom function.

SwiftUI macOS JSON convert in class Codable

I get the following json from an api call but am having trouble making the proper structure in swift and then getting the data as an array.
JSON:
{
"status":"ok",
"users":[
{
"position":0,
"user":{
"pk":"",
"full_name":"",
"username":"",
"profile_pic_url":""
}
},...
]
}
Swift:
class Response: Codable {
var status: String
var users: [User]?
}
class User: Identifiable, Codable {
var uuid = UUID()
var pk: String
var full_name: String
var username: String
var profile_pic_url: String
enum CodingKeys: String, CodingKey {
case
pk = "user.pk",
full_name = "user.full_name",
username = "user.username",
profile_pic_url = "user.profile_pic_url"
}
}
class Fetch: ObservableObject {
#Published var results = [User]()
#Published var resultState = false
#Published var errorState = false
init(url: String) {
self.results = []
let url = URL(string: url)!
URLSession.shared.dataTask(with: url) { data, response, error in
do {
if let data = data {
let results = try JSONDecoder().decode(Response.self, from: data)
DispatchQueue.main.async {
self.results = results.users ?? []
self.resultState = true
}
print("Widget: Ok.")
} else {
self.results = []
self.resultState = true
print("Widget: No data.")
}
} catch {
self.errorState = true
self.resultState = true
print("Widget: Error", error)
}
}.resume()
}
}
Code:
#ObservedObject var fetch = Fetch(url: "")
List(fetch.results) { user in
UserItem(user: user)
}
The problem is that inside array users, it contains an object, this object contains two elements a position attribute and then the user object.
What I think I'm doing wrong is taking the user object.
Can anyone help me out?
Edit:
struct Response: Codable {
let status: String
let users: [UserType]?
}
struct UserType: Codable {
let position: Int
let user: User
}
struct User: Codable {
let pk: String
let full_name: String
let username: String
let profile_pic_url: String
enum CodingKeys: String, CodingKey {
case pk, full_name, username, profile_pic_url
}
}
class Fetch: ObservableObject {
#Published var results = [User]()
#Published var resultState = false
#Published var errorState = false
init(url: String) {
self.results = []
let url = URL(string: url)!
URLSession.shared.dataTask(with: url) { data, response, error in
do {
if let data = data {
let results = try JSONDecoder().decode(Response.self, from: data)
let users = results.users?.map { $0.user }
DispatchQueue.main.async {
self.results = users ?? []
self.resultState = true
}
print("Widget: Ok.")
} else {
self.results = []
self.resultState = true
print("Widget: No data.")
}
} catch {
self.errorState = true
self.resultState = true
print("Widget: Error", error)
}
}.resume()
}
}
List(fetch.results) { user in
UserItem(user: user)
}
You can try this.
struct Response: Codable {
let status: String
let users: [UserWPosition]
var userNoPositions: [UserInfo] { // computed value with only array of userinfo
users.compactMap { $0.user }
}
}
// MARK: - User with position object
struct UserWPosition: Codable {
let position: Int // commenting this will also do no effect
let user: UserInfo
}
// MARK: - UserInfo
struct UserInfo: Codable {
let pk, fullName, username, profilePicURL: String
enum CodingKeys: String, CodingKey {
case pk
case fullName = "full_name"
case username
case profilePicURL = "profile_pic_url"
}
}
Read the comments I added to the code decoding will not code a key that's not added to the struct so commenting out position will have no issue also, the hierarchy of it should be like this now I added a userNoPositions computed value in response to give array of users easily .
Simply to access the array without positions
var resp = try! JSONDecoder().decode(Response.self, from: encoded) // encoded is the data from json
print(resp.userNoPositions) // the array
You need an extra struct that holds the User type
struct UserType: Codable {
let position: Int
let user: User
}
Meaning the top type becomes
struct Response: Codable {
let status: String
let users: [UserType]?
}
You also need to change the CodingKeys enum since it should just contain the property names which mean the enum can be written as
enum CodingKeys: String, CodingKey {
case pk, full_name, username, profile_pic_url
}
For completeness here is the full User type
struct User: Identifiable, Codable {
var uuid = UUID()
var pk: String
var full_name: String
var username: String
var profile_pic_url: String
enum CodingKeys: String, CodingKey {
case pk, full_name, username, profile_pic_url
}
}
and when decoding then you can extract the users array with the map function
do {
let results = try JSONDecoder().decode(Response.self, from: data)
let users = results.users?.map { $0.user }
....
Note that I changed from class to struct because struct is better suited for this but class works as well. I also wonder why the users property is optional, I didn't change that but can the array really be nil?

JSON decoder The data couldn’t be read because it isn’t in the correct format

I am new to this. Somehow I am able to understand how to do this.
I am doing below, but it's giving error- The data couldn’t be read because it isn’t in the correct format.Can someone help me with this? I am stuck on this from past 4 days. I really appreciate.
import SwiftUI
import Foundation
import Combine
struct Movie: Decodable, Identifiable {
var id: Int
var video: String
var vote_count: String
var vote_average: String
var title: String
var release_date: String
var original_language: String
var original_title: String
}
struct MovieList: Decodable{
var results: [Movie]
___________
class NetworkingManager : ObservableObject{
var objectWillChange = PassthroughSubject<NetworkingManager, Never>()
#Published var movies = [Movie]()
init() {
load()
}
func load(){
let url = URL(string: "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=<HIDDEN>")!
URLSession.shared.dataTask(with: url){ (data, response, error) in
do {
if let d = data {
let decodedLists = try JSONDecoder().decode([Movie].self, from: d)
DispatchQueue.main.async {
self.movies = decodedLists
}
}else {
print("No Data")
}
} catch {
print (error.localizedDescription)
}
}.resume()
}
}
This is how the response looks like:
{
"page": 1,
"results": [
{
"id": 419704,
"video": false,
"vote_count": 1141,
"vote_average": 6.2,
"title": "Ad Astra",
"release_date": "2019-09-17",
"original_language": "en",
"original_title": "Ad Astra",
"genre_ids": [
878
],
"backdrop_path": "/5BwqwxMEjeFtdknRV792Svo0K1v.jpg",
"adult": false,
"overview": "An astronaut travels to the outer edges of the solar system to find his father and unravel a mystery that threatens the survival of Earth. In doing so, he uncovers secrets which challenge the nature of human existence and our place in the cosmos.",
"poster_path": "/xJUILftRf6TJxloOgrilOTJfeOn.jpg",
"popularity": 227.167,
"media_type": "movie"
},
]
}
Code should fetch the data and hold in it the array I created. So that I can use it to display in the front end.
I had to consume the exact same API for a similar project and this is how I did it.
When calling:
let response = try JSONDecoder().decode(MovieResponse.self, from: data)
It needs to match the same properties that the JSON response returns.
Below you'll see a MovieResponse struct and the Movie class, which will list all of the properties and return types that the JSON response returns.
The type adopts Codable so that it's decodable using a JSONDecoder instance.
See this official example for more information regarding Codable.
A type that can convert itself into and out of an external representation.
Provided they match then the JSONDecoder() will work to decode the data.
ContentView.swift:
struct ContentView: View {
#EnvironmentObject var movieViewModel: MovieListViewModel
var body: some View {
MovieList(movie: self.movieViewModel.movie)
}
}
MovieListViewModel.swift:
public class MovieListViewModel: ObservableObject {
public let objectWillChange = PassthroughSubject<MovieListViewModel, Never>()
private var movieResults: [Movie] = []
var movie: MovieResults = [Movie]() {
didSet {
objectWillChange.send(self)
}
}
func load(url: String = "https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=<HIDDEN>") {
guard let url = URL(string: url) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
guard let data = data else { return }
let response = try JSONDecoder().decode(MovieResponse.self, from: data)
DispatchQueue.main.async {
for movie in response.results {
self.movieResults.append(movie)
}
self.movie = self.movieResults
print("Finished loading Movies")
}
} catch {
print("Failed to decode: ", error)
}
}.resume()
}
}
MovieResponse.swift:
struct MovieResponse: Codable {
var page: Int
var total_results: Int
var total_pages: Int
var results: [Movie]
}
public class Movie: Codable, Identifiable {
public var popularity: Float
public var vote_count: Int
public var video: Bool
public var poster_path: String
public var id: Int
public var adult: Bool
public var backdrop_path: String
public var original_language: String
public var original_title: String
public var genre_ids: [Int]
public var title: String
public var vote_average: Float
public var overview: String
public var release_date: String
enum CodingKeys: String, CodingKey {
case popularity = "popularity"
case vote_count = "vote_count"
case video = "video"
case poster_path = "poster_path"
case id = "id"
case adult = "adult"
case backdrop_path = "backdrop_path"
case original_language = "original_language"
case original_title = "original_title"
case genre_ids = "genre_ids"
case title = "title"
case vote_average = "vote_average"
case overview = "overview"
case release_date = "release_date"
}
public init(popularity: Float, vote_count: Int, video: Bool, poster_path: String, id: Int, adult: Bool, backdrop_path: String, original_language: String, original_title: String, genre_ids: [Int], title: String, vote_average: Float, overview: String, release_date: String) {
self.popularity = popularity
self.vote_count = vote_count
self.video = video
self.poster_path = poster_path
self.id = id
self.adult = adult
self.backdrop_path = backdrop_path
self.original_language = original_language
self.original_title = original_title
self.genre_ids = genre_ids
self.title = title
self.vote_average = vote_average
self.overview = overview
self.release_date = release_date
}
public init() {
self.popularity = 0.0
self.vote_count = 0
self.video = false
self.poster_path = ""
self.id = 0
self.adult = false
self.backdrop_path = ""
self.original_language = ""
self.original_title = ""
self.genre_ids = []
self.title = ""
self.vote_average = 0.0
self.overview = ""
self.release_date = ""
}
}
public typealias MovieResults = [Movie]
MovieCellViewModel.swift:
public class MovieCellViewModel {
private var movie: Movie
public init(movie: Movie) {
self.movie = movie
}
public func getTitle() -> String {
return self.movie.title
}
// add more properties or functions here
}
MovieCell.swift:
struct MovieCell: View {
var movieCellViewModel: MovieCellViewModel
var body: some View {
Text(self.movieCellViewModel.getTitle())
}
}
MovieList.swift:
struct MovieList: View {
#EnvironmentObject var movieViewModel: MovieListViewModel
var movie: MovieResults
var body: some View {
List(self.movie) { movie in
MovieCell(movieCellViewModel: MovieCellViewModel(movie: movie))
}
}
}
I had a same error, but for the properties inside the struct for JSON object doesn't match JSON file. I fixed this error by setting the property names same as JSON file properties, and in the same order.
Swift file:
struct Coin: Decodable {
var asset_id_base: String
var rates: [CoinDetail]
}
struct CoinDetail: Decodable {
var time: String
var asset_id_quote: String
var rate: Double
}
JSON File:
{
"asset_id_base": "BTC",
"rates": [
{
"time": "2020-11-08T07:50:02.2865270Z",
"asset_id_quote": "CZK",
"rate": 328886.3419989546
},
{
"time": "2020-11-08T07:51:15.0750421Z",
"asset_id_quote": "GPL2",
"rate": 92123.4454168586
},
The Movie struct doesn't match the API response. You missed at least top level, like page and results array. Here you can paste your JSON answer and get the needed struct:
// MARK: - APIAnswer
struct APIAnswer: Codable {
let page: Int
let results: [Result]
}
// MARK: - Result
struct Result: Codable {
let id: Int
let video: Bool
let voteCount: Int
let voteAverage: Double
let title, releaseDate, originalLanguage, originalTitle: String
let genreIDS: [Int]
let backdropPath: String
let adult: Bool
let overview, posterPath: String
let popularity: Double
let mediaType: String
enum CodingKeys: String, CodingKey {
case id, video
case voteCount = "vote_count"
case voteAverage = "vote_average"
case title
case releaseDate = "release_date"
case originalLanguage = "original_language"
case originalTitle = "original_title"
case genreIDS = "genre_ids"
case backdropPath = "backdrop_path"
case adult, overview
case posterPath = "poster_path"
case popularity
case mediaType = "media_type"
}
}
Then you need to use the top level struct, like:
// ...
let decodeResult = try JSONDecoder().decode([APIAnswer].self, from: d)
let movieList = decodeResult.results
// the other advice: don't just take answer and put it to the array.
// API can have errors too, so you can get the array with 2 equal id, for example
update checked your code for second time: you use
let decodeResult = try JSONDecoder().decode([Movie].self, from: d)
instead of:
let decodeResult = try JSONDecoder().decode(MovieList.self, from: d)
P.S. better to attach also the full error from Xcode in future

Swift MVVM how can I implementing Array to model

Array inside the Rss Feed model and how can I solve this problem? I can't get data with MVVM from JSON.
Error: Cannot convert value of type '[Product]' to expected argument type 'Product'
I'm so sorry for bad English.
RSSFeed Model:
struct RSSFeed: Codable {
var title: String?
var description: String?
var icon: String?
var items: [Product]?
}
Product Model:
struct Product: Codable {
var id: String?
var title: String?
var specs: String?
var size: [String]?
var color: String?
var link: String?
var image: [String]?
var price: Price
}
Product View Model:
struct ProductViewModel {
private var product: Product!
init(product: Product) {
self.product = product
}
var id: String {
return product.id ?? ""
}
var title: String {
return product.title ?? ""
}
var specs: String {
return product.specs ?? ""
}
var size: [String] {
return product.size ?? [""]
}
var link: String {
return product.link ?? ""
}
var imageURL: [String] {
return product?.image ?? [""]
}
var price: String {
let rawValue = product.price.rawValue
let currency = product.price.currency
return "\(currency)\(rawValue)"
}
}
Product Provider (Problem is here)
class ProductProvider {
static let product = ProductProvider()
private init() {
}
func getProduct() -> [ProductViewModel] {
var rssFeed = RSSFeed()
let jsonFile = Bundle.main.path(forResource: "products", ofType: "json")
let data = try? Data(contentsOf: URL(fileURLWithPath: jsonFile!))
do {
rssFeed = try JSONDecoder().decode(RSSFeed.self, from: data!)
} catch let error {
print(error.localizedDescription)
}
if let product = (rssFeed.items) {
return ProductViewModel(product: product)
//Error: Cannot convert value of type '[Product]' to expected argument type 'Product'
}
return [ProductViewModel]()
}
}
How can I get JSON data ?
Change the if clause to
if let items = rssFeed.items {
return items.map {ProductViewModel(product: $0)}
}
Since one view model contains one product you need to convert the items array of Product to an array of ProductViewModel using the map function