infernouk infernouk - 25 days ago 4
Swift Question

Loading Serialised JSON Into Table?

Im having a really hard time wrapping my head around this process, I have made an API call and received back JSON, I then use a model to serialise the JSON so it can be used easily in my View Controller Table, but im having issues with how to call the API in the View Controller to have the result fit into my serialisation model. I hope I explained it correctly?

Here is the code of my API Request:

open class ApiService: NSObject {

open func getData(completionHandler: @escaping (NSDictionary?, NSError?) -> Void) -> Self {

let requestUrl = "https://wger.de/api/v2/exercise/?format=json"

Alamofire.request(requestUrl, method: .get, encoding: URLEncoding.default)
.responseJSON { response in
switch response.result {
case .success( let data):

completionHandler(data as? NSDictionary, nil)

case .failure(let error):
print("Request failed with error: \(error)")
completionHandler(nil, error as NSError?)
}
}
return self
}
}


and here is the code of my serialisation

final public class Exercise: ResponseObjectSerializable {
var id: Int!
var description: String!
var name: String!
var muscles: String!
var equipment: String!

public init?(response: HTTPURLResponse, representation: Any) {

guard
let representation = representation as? [String: Any],
let id = representation["id"] as? Int,
let description = representation["description"] as? String,
let name = representation["name"] as? String,
let muscles = representation["muscles"] as? String,
let equipment = representation["equipment"] as? String

else { return nil }

self.id = id
self.description = description
self.name = name
self.muscles = muscles
self.equipment = equipment

}

}


But I cant work out how to fit this into my view controller function call which is currently this

let apiService = ApiService()
let searchController = UISearchController(searchResultsController: nil)

var arrRes: [String] = []
var filtered: [String] = []
var searchActive: Bool = false

var id: Int?
var description: String?
var name: String?
var muscles: String?
var equipment: String?


override func viewDidLoad() {
super.viewDidLoad()
exercisesTableView.delegate = self
exercisesTableView.dataSource = self
exerciseSearchBar.delegate = self
getApiData()
}

func getApiData() {

let _ = apiService.getData() {
(data, error) in
if let data = data {

if let arr = data["results"] as? [String] {

self.arrRes = arr
self.exercisesTableView.reloadData()
}

} else if let error = error {
print(error)
}
}
}

Answer
  • First of all the HTTP response does not affect the custom class at all so I left it out.
  • Second of all the values for keys muscles and equipment are arrays rather than strings.
  • Third of all since the JSON data seems to be immutable declare the properties in the class as constant (let)

With a few slightly changes this is the custom class

final public class Exercise {
   let id : Int
   let description: String
   let name: String
   let muscles : [Int]
   let equipment : [Int]

   public init?(dictionary: [String: Any]) {

      guard
         let id = dictionary["id"] as? Int,
         let description = dictionary["description"] as? String,
         let name = dictionary["name"] as? String,
         let muscles = dictionary["muscles"] as? [Int],
         let equipment = dictionary["equipment"] as? [Int]

         else { return nil }

      self.id = id
      self.description = description
      self.name = name
      self.muscles = muscles
      self.equipment = equipment
   }
}

Then you have to declare the data source array

var exercises = [Exercise]()

And in the method getApiData() populate the array

     ...
     if let results = data["results"] as? [[String:Any]] {
        for result in results {
           if let exercise = Exercise(dictionary: result) {
              self.exercises.append(exercise)
           }
        }
        self.exercisesTableView.reloadData() // might be dispatched on the main thread.
     }

Note: Any is used in Swift 3, in Swift 2 replace all occurrences of Any with AnyObject