Home > front end >  How to create ViewModel from local json with swift higher order function in swift iOS
How to create ViewModel from local json with swift higher order function in swift iOS

Time:01-01

I've a local json and created data model which can parse and get data, I wanted to create a view model from data model to use in the view controller can any suggest how to to with swift higher order functions.

JSON:

{
    "segment": {
        "user1": {
            "banners": {
                "order": 1,
                "items": [1, 3, 4]
            },
            "products": {
                "order": 2,
                "items": [1, 2, 3, 4]
            }
        },
        "user2": {
            "banners": {
                "order": 2,
                "items": [4, 3]
            },
            "products": {
                "order": 1,
                "items": [2, 3, 4]
            }
        }
    },
    "productsList": [{
        "productId": 1,
        "title": "Title of the product",
        "description": "Desciprtion of the product"
    }, {
        "productId": 2,
        "title": "Title of the product",
        "description": "Desciprtion of the product"
    }, {
        "productId": 3,
        "title": "Title of the product",
        "description": "Desciprtion of the product"
    }, {
        "productId": 4,
        "title": "Title of the product",
        "description": "Desciprtion of the product"
    }]
}

Data Model::

struct dataModel : Codable {
    let segment : Segment?
    let productsList : [ProductsList]?
    enum CodingKeys: String, CodingKey {
        case segment = "segment"
        case productsList = "productsList"
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        segment = try values.decodeIfPresent(Segment.self, forKey: .segment)
        productsList = try values.decodeIfPresent([ProductsList].self, forKey: .productsList)
    }
}

struct Banners : Codable {
    let order : Int?
    let items : [Int]?
    enum CodingKeys: String, CodingKey {
        case order = "order"
        case items = "items"
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        order = try values.decodeIfPresent(Int.self, forKey: .order)
        items = try values.decodeIfPresent([Int].self, forKey: .items)
    }
}

struct Products : Codable {
    let order : Int?
    let items : [Int]?
    enum CodingKeys: String, CodingKey {
        case order = "order"
        case items = "items"
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        order = try values.decodeIfPresent(Int.self, forKey: .order)
        items = try values.decodeIfPresent([Int].self, forKey: .items)
    }
}

struct ProductsList : Codable {
    let productId : Int?
    let title : String?
    let description : String?
    enum CodingKeys: String, CodingKey {
        case productId = "productId"
        case title = "title"
        case description = "description"
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        productId = try values.decodeIfPresent(Int.self, forKey: .productId)
        title = try values.decodeIfPresent(String.self, forKey: .title)
        description = try values.decodeIfPresent(String.self, forKey: .description)
    }
}

struct Segment : Codable {
    let user1 : User1?
    let user2 : User1?
    enum CodingKeys: String, CodingKey {
        case user1 = "user1"
        case user2 = "user2"
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        user1 = try values.decodeIfPresent(User1.self, forKey: .user1)
        user2 = try values.decodeIfPresent(User1.self, forKey: .user2)
    }
}

struct User1 : Codable {
    let banners : Banners?
    let products : Products?
    enum CodingKeys: String, CodingKey {
        case banners = "banners"
        case products = "products"
    }
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        banners = try values.decodeIfPresent(Banners.self, forKey: .banners)
        products = try values.decodeIfPresent(Products.self, forKey: .products)
    }
}

In the view model require an array of sections for each user with below conditions.

  1. sections must place based on order key in the json for example if order is 1 then it should be first element in user array and 2 in the sense it should be second element in the user array.
  2. items array represents productId in the productsList array, If the items array has 3 then in the user array it should append prodouctId:3.

With the above two conditions how can I create two array for user1 and and user2. As per above json for example it should return as below.

user1 = [<banners items products>, <products items products>] //Based on order key sorting in array.

user2 = [< products items products>, < banners items products>] //Based on order key sorting in array.

Created ViewModel as below can call viewModel from viewDidLoad and it should return data for each user. Can anyone suggest proper way of doing this with above conditions I spent lot of time to do it wasn't working as expected.

struct viewModel {
    func getUser1Data() -> []{ ///As per json should return banners items as first element and then products items
        
    }
    func getUser2Data() -> [] { ///As per json should return products items as first element and then banners items
        
    }
}

struct User {
    var title: String?
    var description: String?
}

Function to fetch and parse json.

  func getModelFromJson(fileName: String) -> dataModel? {
        let decoder = JSONDecoder()
        let url = Bundle.main.url(forResource: fileName, withExtension: "json")
        let data = try? Data(contentsOf: url!)
        do {
            let result = try decoder.decode(dataModel.self, from: data!)
            return result
        } catch {
            print(error)
        }
        return nil
    }

CodePudding user response:

Here's how I would do it (notes in the code)... I don't know why you mentioned higher order functions, I don't see any reason to use one here...

struct Response: Codable {
    let segment: Segment
    let productsList: [ProductsList]
}

struct ProductsList: Codable {
    let productId: Int
    let title: String
    let description: String
}

struct Segment: Codable {
    let user1: User
    let user2: User
}

struct User: Codable {
    let banners: BannersProducts
    let products: BannersProducts

    // return items that are sorted the way you want.
    var items: [Int] {
        [banners, products]
            .sorted(by: { $0.order < $1.order })
            .flatMap { $0.items }
    }
}

// one type for both Banners and Products
struct BannersProducts: Codable {
    let order: Int
    let items: [Int]
}

func getUsersFromJson(fileName: String) -> (user1: [ProductsList], user2: [ProductsList]) {
    let decoder = JSONDecoder()
    let url = Bundle.main.url(forResource: fileName, withExtension: "json")!
    let data = try! Data(contentsOf: url)
    let response = try! decoder.decode(Response.self, from: data)
    // put the products in a dictionary for easy lookup
    let products = Dictionary(grouping: response.productsList, by: { $0.productId })
    let user1 = response.segment.user1.items.map { products[$0]!.first! } // user1 products
    let user2 = response.segment.user2.items.map { products[$0]!.first! }// user2 products
    return (user1, user2)
}
  • Related