swiftswifty-json

SwiftyJSON data is only using the first element of my json


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.sstatic.net/9ZlRy.png?s=256

Solution

  • 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.