如何在 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`