Alamofire error to decode JSON response with Struct - json

I have a problem with my alamofire.request. The response is nil but I don't understand why.
My problem are: I use API of OpenFoodFacts, it works with a web address of this style: https://fr.openfoodfacts.org/api/v0/produit/3366321054229
I use Alamofire to get the request with a protocol, a session and a service.
My Protocol:
protocol CheckerFridgeProtocol {
func request(url: URL, parameters: Parameters, callback: #escaping (DataResponse<Any>) -> Void )
}
My Session:
class CheckerFridgeSession: CheckerFridgeProtocol {
func request(url: URL, parameters: Parameters, callback: #escaping (DataResponse<Any>) -> Void) {
Alamofire.request(url, method: .get, parameters: parameters).responseJSON { responseData in
callback(responseData)
}
}
}
My service:
class CheckerFridgeService {
private var checkerFridgeSession: CheckerFridgeSession
init(checkerFridgeSession: CheckerFridgeSession = CheckerFridgeSession()) {
self.checkerFridgeSession = checkerFridgeSession
}
// get recipe ingredients
func getRequest(barCode: String, callback: #escaping (Bool, CheckerFridgeData?) -> Void) {
let parameters: Parameters = [:]
let url = URL(string: "https://fr.openfoodfacts.org/api/v0/produit/\(barCode)")!
checkerFridgeSession.request(url: url, parameters: parameters) { (response) in
DispatchQueue.main.async {
guard response.response?.statusCode == 200 else {
print("Response 400 Error")
callback(false, nil)
return
}
guard let data = response.data else {
print("Data error")
callback(false, nil)
return
}
guard let responseJSON = try? JSONDecoder().decode(CheckerFridgeData.self, from: data) else {
print("Response JSON Failed")
callback(false, nil)
return
}
guard responseJSON.status > 0 else {
print("Status Code 0")
callback(false, nil)
return
}
// print(response.result.value as Any)
callback(true, responseJSON)
}
}
}
}
I have a file CheckerFridgeData contain Struct to decode the responseJSON:
// MARK: - CheckerFridge
struct CheckerFridgeData: Codable {
let status: Int
let product: Product?
}
// MARK: - Product
struct Product: Codable {
let additivesTags: [String]?
let productNameFr: String?
let imageSmallURL, imageFrontSmallURL: String?
}
When I use my request to my code, I have no error but my responseJSON return nil
CheckerFridgeData(status: 1, product: Optional(Checker_Fridge.Product(additivesTags: nil, productNameFr: nil, imageSmallURL: nil, imageFrontSmallURL: nil)))
But if I print response.result.value as Any the code returns the JSON Value.
Have you any idea why I can't decode my struct with my JSON data ?
Thanks a lot for your help

I think the problem is about custom types. If you'd like to convert custom types you need to use CodingKeys protocol. When I looked your json there is no property like additivesTags there is additives_tags so you should use CodingKeys like following.
struct Product: Codable {
let additivesTags: [String]?
let productNameFr: String?
let imageSmallURL, imageFrontSmallURL: String?
enum CodingKeys: String, CodingKey {
case additivesTags = "additive_tags"
case productNameFr = "product_name_fr"
case imageSmallURL
}
}

Related

"Expected to decode Array<Any> but found a dictionary instead." How can i solve this

i'm trying to do with API Call. I got error every time trying to do API Call.
typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
this is what i see when simulate my code in console.
Here is json format i try to call in my app. Click
My Model
struct Article: Codable {
let author: String
let title, articleDescription: String
let url: String
let urlToImage: String
let publishedAt: Date
let content: String?
enum CodingKeys: String, CodingKey {
case author, title
case articleDescription = "description"
case url, urlToImage, publishedAt, content
}
}
and This is my API Call function.
import UIKit
class ViewController: UIViewController {
var article = [Article]()
override func viewDidLoad() {
super.viewDidLoad()
jsonParse {
print("success")
}
view.backgroundColor = .red
}
func jsonParse(completed: #escaping () -> ()) {
let url = URL(string: "https://newsapi.org/v2/top-headlines?country=tr&apiKey=1ea9c2d2fbe74278883a8dc0c9eb912f")
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
if error != nil {
print(error?.localizedDescription as Any)
}else {
do {
let result = try JSONDecoder().decode([Article].self, from: data!)
DispatchQueue.main.async {
print(data as Any)
print("success")
self.jsonParse {
print("success")
}
}
}catch {
print(error.localizedDescription)
}
}
}
task.resume()
}
}
Can you help me about my problem, thank you.
This is a very common mistake: You ignore the root object, a dictionary. With Decodable it's mandatory to decode the JSON from the top.
Add this struct
struct Response: Decodable {
let status: String
let totalResults: Int
let articles: [Article]
}
and decode
let result = try JSONDecoder().decode(Response.self, from: data!)
And never print(error.localizedDescription) in a Codable catch block. Always write this
} catch {
print(error)
}

Unable to decode JSON data while parsing in swift

I am using generic codable protocol for JSON parsing
below is the JSON structure and postman response
{
"jsonrpc": "2.0",
"result": {
"categories": [
{
"id": 91,
"title": "ADMINISTRATION & SECRETARIAL",
},
{
"id": 62,
"title": "BEAUTY",
},
{
"id": 325,
"title": "CARE",
for above response i have created model like below:
struct CategoryModel: Codable {
let jsonrpc: String?
let result: ResultCat?
}
struct ResultCat: Codable {
let categories: [Categorymine]?
}
struct Categorymine: Codable {
let id: Int?
let title, slug: String?
}
code: in this code i am getting response above switch response.result case and data also getting (11113 bytes).. but here i am unable to decode data i think.. when i call service not getting response
please guide me to get response
first time i am working with generics, where am i wrong. please guide me
import Foundation
import Alamofire
class GeneralResponse<T: Codable>: Codable {
var jsonrpc: String
var result: T?
}
struct RequestObject {
var method: HTTPMethod
var path: String
var token: String?
var params: [String: Any]?
}
class WebService {
private let decoder: JSONDecoder
public init(_ decoder: JSONDecoder = JSONDecoder()) {
self.decoder = decoder
}
public func serviceCall<T: Codable>(_ objectType: T.Type,
with request: RequestObject,
completion: #escaping (GeneralResponse<T>?, Error?) -> Void) {
var headerParams: HTTPHeaders?
AF.request(request.path, method: request.method, parameters: request.params,encoding: JSONEncoding.default, headers: headerParams)
.responseJSON { response in
print("Json response: \(response)")//getting response here as well
switch response.result {
case .success(let dictData):
let JSON = dictData as? [String : Any]
do {
let data = response.data
print("only data \(data)")//Optional(11113 bytes)
let responseData = try self.decoder.decode(GeneralResponse<T>.self, from: data ?? Data())
print("model data \(String(describing: responseData.result))")//Optional(TestigAllInOne.CategoryModel(jsonrpc: nil, result: nil))
} catch {
completion(nil, error)
}
case .failure(let error):
let error = error
print(error.localizedDescription)
}
}
}
}
in console i am getting like this:
only data Optional(11113 bytes)
model data Optional(TestigAllInOne.CategoryModel(jsonrpc: nil, result: nil))
i am using service call in viewcontroller like this:
var catData: CategoryModel? {
didSet {}
}
func genericCall(){
let request = RequestObject(method: .get, path: "https://tt.com/dev/api/get-category", params: nil)
WebService().serviceCall(CategoryModel.self, with: request) { (response, error) in
if let items = response?.result {
print("viewcontroller response \(items)")
DispatchQueue.main.sync {
self.catData = items
self.tableView.reloadData()
}
}
else{
print(error)
}
}
}
Acording to your Model T should be of type ResultCat and not CategoryModel
WebService().serviceCall(ResultCat.self, with: request)
look at your generic class:
class GeneralResponse<T: Codable>: Codable {
var jsonrpc: String
var result: T?
}
so T in this context is the type of the result var.
Edit to address the comment:
if i want to pass whole CategoryModel in serviceCall then how to change my code.
I asume you want to call the function as described in the question. To achieve this simply get rid of your generic struct as you wouldn´t use it in that case.
public func serviceCall<T: Codable>(_ objectType: T.Type,
with request: RequestObject,
completion: #escaping (T?, Error?) -> Void) {
and:
let responseData = try self.decoder.decode(T.self, from: data ?? Data())

Swift JSON with dynamic Keys

I am trying to parse JSON using Swift, which has dynamic keys. Tried several ways but still did not find the solution. Could you please help me ?
I am trying to parse NativeName, which is dynamic based on which language country name is present.
API: https://restcountries.com/v3.1/all
struct Objects: Codable {
let name: Name
let cca2 : String
let flag: String
}
struct Name: Codable {
let common, official: String
let nativeName: NativeName
}
struct NativeName: Codable {
var deu : Deu
}
struct Deu: Codable {
let official, common: String?
}
and here is JSON Model:
class ParsingService {
static let shared = ParsingService()
func fetchData() {
guard let url = URL(string: "https://restcountries.com/v3.1/all") else {
print("DEBUG: URL is nill")
return}
let session = URLSession.shared
let task = session.dataTask(with: url) { data, _, error in
guard let retrievedData = data, error == nil else {
print("DEBUG: Data is not available")
return}
print("DEBUG: Data is available \(retrievedData)")
guard let decodedData = self.JSONParsing(inputData: retrievedData) else {
print("DEBUG: Missing data")
return}
print("DEBUG: Data is there")
print("DEBUG: \(decodedData[0].cca2)")
print("DEBUG: \(decodedData[0].flag)")
print("DEBUG: \(decodedData[0].name.nativeName.deu.official)")
DispatchQueue.main.async {
print(decodedData.currencies)
}
}
task.resume()
}
func JSONParsing(inputData: Data)-> [Objects]? {
let decoder = JSONDecoder()
do {
let data = try? decoder.decode([Objects].self, from: inputData)
return data
} catch {
print("DEBUG: Cannot get data")
return nil
}
}
}
you could try this approach:
struct ContentView: View {
var body: some View {
Text("testing")
.onAppear {
fetchData() { results in
print("---> results: \(results.count) \n")
for i in 0..<3 {
print("---> results[\(i)]: \(results[i].name.nativeName)")
}
}
}
}
// todo deal with errors
func fetchData(completion: #escaping ([Objects]) -> Void) {
let url = URL(string: "https://restcountries.com/v3.1/all")
guard let url = url else { completion([]); return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { completion([]); return }
do {
let results = try JSONDecoder().decode([Objects].self, from: data)
completion(results)
}
catch {
print("Error: \(error)")
completion([])
}
}.resume()
}
}
struct Objects: Codable {
let name: Name
let cca2 : String
let flag: String
}
struct Deu: Codable {
let official, common: String?
}
struct Name: Codable {
let common, official: String
let nativeName: NativeName? // <-- here
}
// -- here
struct NativeName: Codable {
var lang: [String: Deu]
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
lang = try container.decode([String: Deu].self)
}
func encode(to encoder: Encoder) throws {
// todo
}
}
Note, you could also use a Tuple, such as var lang: (key: String, value: Deu)

SWIFT: Trying to decode JSON ,it returns nil

When you i try to call .decode() to decode a struct, it returns nil.Can anyone help me?
My app is returning null value from the JSON data, from this line of code:
let newPosData = try JSONDecoder().decode(NewPosDataBase.self, from: responseData)
Here is complete code:
func getNewLFNREXT(invnr: String, completionHandler: #escaping (String, Bool, NewPosDataBase?) -> Void) {
let headers: HTTPHeaders = [
.authorization(username: UserCredentials.shared.username, password: UserCredentials.shared.password),
.accept("application/json")
]
let url:String = Config.middleware + Config.url + "/mobile/lfnrext?invnr=\(invnr)"
AF.request(url, method: .get, parameters: [:], headers: headers).responseJSON { response in
if response.response?.statusCode == 200 {
do {
guard let responseData = response.data else {
completionHandler(errorMessage, false, nil )
return
}
let newPosData = try JSONDecoder().decode(NewPosDataBase.self, from: responseData)
print( newPosData.newPosData?.newLfnrext)
completionHandler("", true, newPosData)
} catch {
completionHandler(errorMessage, false , nil )
}
}else {
let message = self.getErrorMessageFrom(data: response.data, defaultErrorMessage: errorKeineDaten)
completionHandler(message, false, nil)
}
}
}
You need to add the enum CodingKeys if the keys' name differ from the property names of the Codable type.
So, your models should be like,
class NewPosDataBase: Codable {
enum CodingKeys: String, CodingKey {
case newPosData = "NEW_POS_DATA"
}
//rest of the code...
}
class NewPosData: Codable {
enum CodingKeys: String, CodingKey {
case newLfnrext = "NEW_LFNREXT"
case aktuellerZahler = "AKTUELLER_ZAHLER"
case gpsStd = "GPS_STD"
}
//rest of the code...
}
Note: There is no need to give any init implementation in the models unless you need them for any specific purpose.

How to get value in json Swift

I hava a json file:
jsonpElixo({
"response":{
"parks":[
{
"Park":{
"id":"2",
"name":"PARQUE VILLA-LOBOS",
"type":"Urbano"
},
"Address":{
"lat":"-23.547245206920508",
"long":"-46.71627699999999",
"cep":null,
"street":"Avenida Professor Fonseca Rodrigues",
"number":"1025",
"neighborhood":"Alto Pinheiros",
"city":"S\u00e3o Paulo",
"state":"SP",
"id":"9"
}
}
]
}
})
But I can't get the elements inside the {}. Because the "jsonpElixo(" is breaking during the decodable.
How can I fix that?
The func to get info about json file.
func getParks() {
var request = URLRequest(url:gitUrl)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let welcome = try? decoder.decode(Welcome.self, from: data)
} catch let err {
print("Err", err)
}
}.resume()
}
The structs to Decodable the elements. But I dont know how can I scape this first element ("jsonpElixo(")
struct Welcome: Decodable {
let response: Response
}
struct Response: Decodable {
let parks: [Park]
}
struct Park: Decodable {
let park: ParkClass
let address: Address
enum CodingKeys: String, CodingKey {
case park = "Park"
case address = "Address"
}
}
struct Address: Decodable {
let lat, long: String
let cep: String?
let street, number, neighborhood, city: String
let state, id: String
}
struct ParkClass: Decodable {
let id, name, type: String
}
You can create a function that will remove the outer jsonpElixo() object and return the json to work with.
Start with an extension on Data so we can create something similar to this:
extension Data {
func decodeJsonpElixo() -> Data {
guard let jsonpString = String(data: self, encoding: .utf8) else {return self}
if jsonpString.hasPrefix("jsonpElixo(") && jsonpString.hasSuffix(")") {
var decoderString = jsonpString.replacingOccurrences(of: "jsonpElixo(", with: "")
decoderString.remove(at: String.Index(encodedOffset: decoderString.endIndex.encodedOffset - 1))
return Data(decoderString.utf8)
}
return self
}
}
Then you can use this in your URLSession closure like this:
guard let data = data else { return }
let decoderData = data.decodeJsonpElixo()
let decoder = JSONDecoder()
do {
let welcome = try decoder.decode(Welcome.self, from: decoderData)
} catch let err {
print(err)
}