Проблемы с Swift ForEach со словарем JSON - PullRequest
0 голосов
/ 24 марта 2020

Я пытаюсь создать приложение, которое получает объект JSON от конечной точки API, который затем я хочу перечислить в представлении. Я смотрел много видео на эту топику c, но в каждом видео они используют очень простые c 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, но я знаю, о чем некоторые из них спрашивают, в частности о соответствии идентифицируемому, но он говорит, что struct ActivitiesModel не соответствует идентифицируемому при попытке добавить тег и объект JSON У меня нет идентификатора для этого раздела, поэтому я не могу попросить его сделать его идентифицируемым.

Любая помощь будет принята с благодарностью, сейчас это своего рода стена.

Вот 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.",
            },
        }
    }
}

1 Ответ

1 голос
/ 24 марта 2020

У вас есть несколько проблем здесь.

Во-первых, словари и List s не совместимы; Вам действительно нужен массив. Как говорят ошибки, элемент, который передается на List, должен быть подтвержден на Identifiable и RandomAccessCollection. Словарь не соответствует ни одному, и вы не можете заставить его сделать это.

Ваша вторая проблема заключается в том, что fetcher.activities является необязательным, а инициализатор List не может принять необязательный. Компилятор предлагает несколько альтернатив - укажите значение по умолчанию, используя оператор объединения nil (??), или принудительно разверните, используя ! (что приведет к sh, поскольку вы знаете, что fetcher.activities будет nil изначально.

Причина первой проблемы root заключается в том, что у вашего JSON есть переменные ключи, а не простой набор действий. Это не очень хорошая идея, и вам действительно нужно немного поработать на стороне сервера, чтобы исключить бессмысленный ключ цифр c и иметь простой массив, как я отмечал, но вы указали, что не можете этого сделать.

Учитывая это, другой подход заключается в раскрытии значения словаря в виде массива с использованием вычисляемого свойства:

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

Пока мы здесь, мы можем исправить это un-Swifty activity_desc с помощью перечисления CodingKeys:

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)
            }
        }
    }
}
...