SwiftyJSON data is only using the first element of my json

180 Views Asked by At

I'm using Alamofire and SwiftyJSON and I'm getting my data, but my view is only repeating the data for the first element of the JSON. The data should be Monday, Tuesday, Wednesday, and so on

[![enter image description here][1]][1]

Here's the class I'm using

class observer : ObservableObject {

    @Published var datas = [datatype]()

    init() {

        AF.request("https://api.npoint.io/e667b934a476b8b88745").responseData { (data) in

            let json = try! JSON(data: data.data!)
            let trainingDay = json["weekExercise"].arrayValue.map
            {$0["exercise"].stringValue}
            print(trainingDay)

            for i in json["weekExercise"]{

                self.datas.append(datatype(id: i.1["weeknumber"].intValue,
                                           day: i.1["day"].stringValue,
                                           exercise: i.1["exercise"].stringValue,
                                           dayMiles: i.1["dayMiles"].intValue))

            }
        }
    }
}

My data looks like this:

{
    "weeknumber": 1,
    "weekExercise": [
      {
            "day": "Monday",
            "dayMiles": 6,
            "exercise": "6 miles"
        },
        {
            "day": "Tuesday",
            "dayMiles": 9,
            "exercise": "12 x 400m WU/CD"
        },
        {
            "day": "Wednesday",
            "dayMiles": 0,
            "exercise": "Rest"
        },
        {
            "day": "Thursday",
            "dayMiles": 6,
            "exercise": "6 miles"
        },
        {
            "day": "Friday",
            "dayMiles": 6,
            "exercise": "6 miles"
        },
        {
            "day": "Saturday",
            "dayMiles": 6,
            "exercise": "6 miles"
        },
        {
            "day": "Saturday",
            "dayMiles": 8,
            "exercise": "8 miles"
        }
    ],
    "totalWeekMiles": 41,
    "planName": "Hanson Method Advance"
}



  [1]: https://i.stack.imgur.com/9ZlRy.png?s=256
1

There are 1 best solutions below

1
vadian On BEST ANSWER

My suggestion is to take advantage of Alamofire's support of Codable and Combine. SwiftyJSON is outdated and not needed anymore.

import Combine
import Alamofire

struct ExerciseData : Codable, Identifiable {
    let id : Int
    let weeknumber : Int
    let weekExercise : [Exercise]
}

struct Exercise : Codable, Identifiable {
    let id = UUID()
    let day: String
    let dayMiles: Int
    let exercise: String
    
    private enum CodingKeys : String, CodingKey { case day, dayMiles, exercise}
}

class Observer : ObservableObject {
    
    private var subscription : AnyCancellable?

    @Published var exercises = [Exercise]()

    init() {
        subscription = AF.request("https://api.npoint.io/e667b934a476b8b88745")
            .publishDecodable(type: [ExerciseData].self, queue: .global())
            .result()
            .receive(on: DispatchQueue.main)
            .map{ result -> [Exercise] in
                switch result {
                    case .success(let data) : return data.first?.weekExercise ?? []
                    case .failure: return []
                }
            }
            .sink(receiveCompletion: { _ in
                self.subscription = nil // this breaks the retain cycle
            }, receiveValue: { exercises in
                self.exercises = exercises
            })
        
    }
}

You can even remove Alamofire in favor of the built-in data task publisher

import Combine

class Observer : ObservableObject {
    
    private var subscription : AnyCancellable?

    @Published var exercises = [Exercise]()

    init() {
        let url = URL(string: "https://api.npoint.io/e667b934a476b8b88745")!
        subscription = URLSession.shared.dataTaskPublisher(for: url)
            .receive(on: DispatchQueue.main)
            .map(\.data)
            .decode(type: [ExerciseData].self, decoder: JSONDecoder())
            .map{$0.first?.weekExercise ?? []}
            .replaceError(with: [])
            .sink(receiveCompletion: { _ in
                self.subscription = nil
            }, receiveValue: { exercises in
                self.exercises = exercises
            })
    }
}

The error handling is rudimentary. It returns an empty array in case of an error.