I know there are various threads on this but it seems like there's a fundamental flaw in my understanding of objects in Swift.
So I have the following function that returns a JSON response as expected:
func executeGet( completion: @escaping ([[String:Any]]?, Error?) -> Void) {
AF.request("https://myURL",
method:.get,
headers:headers).responseJSON{ response in
debugPrint(response) <- I get readable JSON response from here
if let error = response.error {
completion(nil, error)
}
else if let jsonArray = response.value as? [[String:Any]]{
completion(jsonArray, nil)
}
else if let jsonDict = response.value as? [String:Any]{
completion([jsonDict], nil )
}
}
}
And then I'm able to leverage the completion handler to read the JSON response outside of AL's scope:
executeGet() { (json, error) in
if let error = error{
print(error.localizedDescription)
}
else if let json = json {
print(type(of:json)) <- did this for sanity check
print(json["result"][0]["capturedlists"]) <- Error No exact matches in call to subscript
}
}
But I'm unable to just get the 'capturedLists' part of the JSON. The JSON snippet I need to capture looks as follows:
"capturedLists":{"Something":
[{"Position":"1","A1":"a1","B1":"b1"},
{"Position":"2","A2":"a2","B2":"b2"}]}}}
Can anyone help me figure out what am I doing wrong? I know usually you can get data from Json by something like response[a][0] but that doesn't work here for some reason.
EDIT: Upone suggestion I used Quicktype to get a struct for my response and got the following:
struct Welcome: Codable {
let statusCode: Int
let messageCode: String
let result: Result
}
// MARK: - Result
struct Result: Codable {
let id: String
let inputParameters: InputParameters
let robotID: String
let runByUserID, runByTaskMonitorID: JSONNull?
let runByAPI: Bool
let createdAt, startedAt, finishedAt: Int
let userFriendlyError: JSONNull?
let triedRecordingVideo: Bool
let videoURL: String
let videoRemovedAt: Int
let retriedOriginalTaskID: String
let retriedByTaskID: JSONNull?
let capturedDataTemporaryURL: String
let capturedTexts: CapturedTexts
let capturedScreenshots: CapturedScreenshots
let capturedLists: CapturedLists
enum CodingKeys: String, CodingKey {
case id, inputParameters
case robotID = "robotId"
case runByUserID = "runByUserId"
case runByTaskMonitorID = "runByTaskMonitorId"
case runByAPI, createdAt, startedAt, finishedAt, userFriendlyError, triedRecordingVideo
case videoURL = "videoUrl"
case videoRemovedAt
case retriedOriginalTaskID = "retriedOriginalTaskId"
case retriedByTaskID = "retriedByTaskId"
case capturedDataTemporaryURL = "capturedDataTemporaryUrl"
case capturedTexts, capturedScreenshots, capturedLists
}
}
.responseJSON
is deprecated. Don't use it.
The error occurs because json["result"]
returns Any
but the compiler must know the static type for the subsequent index subscription.
As you have a Decodable
model AF provides responseDecodable
. Change your function to
func executeGet( completion: @escaping (Result<Welcome,Error>) -> Void) {
AF.request("https://myURL",
method:.get,
headers:headers).responseDecodable(of: Welcome.self) { response in
switch response.result {
case .success(let welcome):
completion(.success(welcome))
case .failure(let error):
completion(.failure(error))
}
}
}
which can even be simplified to
func executeGet( completion: @escaping (Result<Welcome,Error>) -> Void) {
AF.request("https://myURL",
method:.get,
headers:headers).responseDecodable(of: Welcome.self) { response in
completion(response.result)
}
}
And call it
executeGet() { result in
switch result {
case .success(let welcome): print(welcome)
case .failure(let error): print(error)
}
}
If you get an error the model doesn't match the JSON and the error tells you exactly what's wrong