【问题标题】:Swift ForEach Issues with JSON DictionaryJSON 字典的 Swift ForEach 问题
【发布时间】:2020-07-04 09:14:28
【问题描述】:

我正在尝试构建一个从 API 端点接收 JSON 对象的应用程序,然后我想在视图中列出该对象。我看过很多关于这个主题的视频,但是在每个视频中,他们都使用非常简单的 JSON 对象作为示例,因此他们编写的代码似乎并没有真正转移,无论我如何尝试格式化它都会给我错误.代码如下

import SwiftUI
import Combine
import Foundation

public struct ActivityModel: Codable, Identifiable {
    public let id: Int
    public let name: String
    public let activity_desc: String?
}


public struct ActivitiesModel2: Codable {
    public let location: String
    public let popular: [String:ActivityModel]
}

public struct ActivitiesModel: Codable {
    public let activities: ActivitiesModel2
}

public class ActivityFetcher: ObservableObject {
    var activities: ActivitiesModel?

    init(){
        guard let url = URL(string: "https://mywebsite.com/api/loadapi") else { return }

        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"

        URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in

            do {
                if let d = data {
                    let decodedLists = try JSONDecoder().decode(ActivitiesModel.self, from: d)
                    DispatchQueue.main.async {
                        self.activities = decodedLists
                    }
                } else {
                    print("No Data")
                }
            } catch {
                print("Error")
            }

        }.resume()
    }
}

struct ActivityGuestView: View {
    let networkingServiceGeneral = NetworkingServiceGeneral()

    @ObservedObject var viewRouter: ViewRouter

    @ObservedObject var fetcher = ActivityFetcher()

    var body: some View {
        // This is where my issues start
        List(fetcher.activities?.activities.popular) { result in
            VStack {
                Text(result.name)
                Text(result.activity_desc)
                    .font(.system(size: 11))
                    .foregroundColor(Color.gray)
            }
        }
    }
}

正如我所说,这段代码给了我 5 个错误。它们是:

    - Initializer 'init(_:rowContent:)' requires that '(key: String, value: ActivityModel)' conform to Identifiable
    - Initializer 'init(_:rowContent:)' requires that '[String : ActivityModel]' conform to 'RandomAccessCollection'
    -Value of optional type '[String : ActivityModel]?' must be unwrapped to a value of type '[String : ActivityModel]'
    - Coalesce using '??' to provide a default when the optional value contains 'nil'
    - Force-unwrap using '!' to abort execution if the optional value contains 'nil'

其中一些错误有修复它的选项,但是当我按下修复它会添加代码但实际上并没有修复错误,所以我想还是只包含它们。我对 Swift 还是很陌生,但我知道其中的一些要求,特别是符合 Identifiable 的要求,但它说当我尝试添加标签时 struct ActivitiesModel 不符合 identifiable 并且 JSON 对象没有没有该部分的 ID,所以我不能要求 ID 使其可识别。

任何帮助将不胜感激,这已经是一堵墙了。

这是 JSON

"activities": {
        "location": "Dallas",
        "popular": {
            "10": {
                "id": 38,
                "name": "Adventure Landing Dallas",
                "activity_desc": "Aquatic complex chain with additional land attractions including mini-golf, laser tag & go-karts.",
            },
            "12": {
                "id": 40,
                "name": "Jumpstreet",
                "activity_desc": "None provided.",
            },
        }
    }
}

【问题讨论】:

  • 好的,这里发生了一些事情。首先,带有可变键的 JSON 非常糟糕。如果可以的话,改变它; popular 应该只是一系列活动。如果你不能改变它,那么你可以在你的结构中使用一个计算属性来返回一个ActivityModel 的数组。其次,您需要处理可选的,最好在获取数据并且有要列出的内容之前甚至不进入此视图,但是您可以使用 nil 合并运算符来提供空数组
  • 最后我建议你使用编码键来消除 ``activity_desc` 中的 un-Swifty _ 并将其映射到 activityDesc
  • 很多这些术语都让我头疼...我无法将 JSON 输出更改为没有动态标签,因为它是服务器端的 SQL 返回

标签: ios arrays json swift combine


【解决方案1】:

这里有几个问题。

首先,字典和Lists 不兼容;你真的需要一个数组。正如错误所说,提供给List 的项目需要确认IdentifiableRandomAccessCollection。字典既不符合,也无法真正做到。

您的第二个问题是 fetcher.activities 是可选的,List 初始化程序不能接受可选的。编译器建议了几个替代方案 - 使用 nil 合并运算符 (??) 提供默认值或使用 ! 强制展开(这会崩溃,因为您知道 fetcher.activities 最初将是 nil .

第一个问题的根本原因是您的 JSON 具有可变键,而不是简单的活动数组。根据我的评论,这不是一个好主意,您应该真正在服务器端进行一些工作以消除无意义的数字键并拥有一个简单的数组,但是您已经表明您不能这样做。

鉴于此,另一种方法是使用计算属性将字典值公开为数组:

public struct ActivitiesModel2: Codable {
    public let location: String
    private var popular: [String:ActivityModel] 
    public var popularActivities: [ActivityModel] {
        get {
            return Array(self.popular.values)
        }
    }
}

当我们在这里的时候,我们可以用 CodingKeys 枚举来修复那个 un-Swifty activity_desc

public struct ActivityModel: Codable, Identifiable {
    public let id: Int
    public let name: String
    public let activityDesc: String?

    enum CodingKeys: String, CodingKey {
        case id = "id"
        case name = "name"
        case activityDesc = "activity_desc"
    }
}

现在,您可以重新编写视图以使用新的计算属性并处理可选属性:

struct ActivityGuestView: View {
    let networkingServiceGeneral = NetworkingServiceGeneral()

    @ObservedObject var viewRouter: ViewRouter

    @ObservedObject var fetcher = ActivityFetcher()

    var body: some View {
        // This is where my issues start
        List(fetcher.activities?.activities.popularActivities ?? []) { result in
            VStack {
                Text(result.name)
                Text(result.activity_desc)
                    .font(.system(size: 11))
                    .foregroundColor(Color.gray)
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-12
    相关资源
    最近更新 更多