如何在 swift iOS 中使用 swift 高阶函数从本地 json 创建 ViewModel

Posted

技术标签:

【中文标题】如何在 swift iOS 中使用 swift 高阶函数从本地 json 创建 ViewModel【英文标题】:How to create ViewModel from local json with swift higher order function in swift iOS 【发布时间】:2021-12-31 11:45:45 【问题描述】:

我有一个本地 json 并创建了可以解析和获取数据的数据模型,我想从数据模型创建一个视图模型以在视图控制器中使用任何建议如何使用 swift 高阶函数。

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"
    ]

数据模型::

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)
    

在视图模型中,每个用户都需要一个具有以下条件的部分数组。

    部分必须基于 json 中的 order 键放置,例如,如果 order 为 1,那么它应该是用户数组中的第一个元素,而 2 在某种意义上它应该是用户数组中的第二个元素。 items 数组表示 productsList 数组中的 productId,如果 items 数组有 3 则在 user 数组中应该附加 prodouctId:3。李>

在以上两个条件下,如何为 user1 和 user2 创建两个数组。以上面的 json 为例,它应该返回如下。

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.

如下创建的 ViewModel 可以从 viewDidLoad 调用 viewModel,它应该为每个用户返回数据。任何人都可以建议在上述条件下执行此操作的正确方法我花了很多时间来做它没有按预期工作。

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?

获取和解析 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
    

【问题讨论】:

只有两个用户吗? 是的,只有用户。 【参考方案1】:

我会这样做(代码中的注释)...我不知道您为什么提到高阶函数,我看不出有任何理由在这里使用它...

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)

【讨论】:

Dictionary(grouping: response.productsList, by: \.productId) 谢谢我根据您的想法对其进行了一些更改,它可以按预期工作。 同意。 Codeable 协议消除了解析中的大量繁琐工作,尽管可能并不详尽。

以上是关于如何在 swift iOS 中使用 swift 高阶函数从本地 json 创建 ViewModel的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 如何在 swift 中使用`NSMutableString`

如何在 swift iOS 中使用 swift 高阶函数从本地 json 创建 ViewModel

如何使用 Swift 在 iOS 中同时录制和播放音频?

如何在 Swift 2 的 iOS 应用程序中使用音频?

iOS:面向 iOS 9 时使用 Swift 4

如何在 Flutter 插件的 Swift 编写的 iOS 部分中使用 Objective-C 框架