Unable to decode JSON data while parsing in swift - json

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())

Related

Unable to get JSON response for post API with alamofire in swift

I have created generic methods in NetworkManager but this serviceCall working only .get method APIs
if i use post method with parameters like below got error
error:
{
code = "-32700";
meaning = "Undefined array key \"params\"";
message = "Parse error";
}
func testServiceCall(){
let parameters = ["name": "testinggg", "job": "manager"] as [String : Any]
let request = RequestObject(params: parameters, method: .post, path: "https://reqres.in/api/users", isTokenNeed: false, vc: self)
WebService.sharedInstance.serviceCall(UserModel.self, with: request) { (response, error) in
print("user post response \(response)")
}
}
This is NetworkManager Class: here added parameters not working
class GeneralResponse<T: Codable>: Codable {
var jsonrpc: String
var result: T?
let error: ErrorClass?
}
struct ErrorClass: Codable {
let code, message, meaning: String?
}
struct RequestObject {
var params: [String: Any]?
var method: HTTPMethod
var path: String
var isTokenNeed: Bool = false
var vc: UIViewController?
}
class NetworkManager {
private let decoder: JSONDecoder
static let sharedInstance = NetworkManager()
public init(_ decoder: JSONDecoder = JSONDecoder()) {
self.decoder = decoder
}
public func serviceCall<T: Codable>(_ objectType: T.Type,
with request: RequestObject,
completion: #escaping (T?, Error?) -> Void) {
AF.request(request.path, method: request.method, parameters: request.params,encoding: JSONEncoding.default, headers: request.isTokenNeed ? ["Authorization" : "Bearer hnjjdshjdsh", "Accept": "application/json"] : ["" : ""])
.responseJSON { response in
switch response.result {
case .success(_):
do {
let data = response.data
let responseData = try self.decoder.decode(T.self, from: data ?? Data())
completion(responseData, nil)
} catch {
completion(nil, error)
print("in catch \(error.localizedDescription)")
}
case .failure(let AFError):
let error = AFError
print(error.localizedDescription)
print("failure error: \(error)")
}
}
}
}

SwiftyJSON convert string array to array not working

I have data as below:
data =
[
"\u65b0\u5317\u5e02\u4e09\u91cd\u5340","\u65b0\u5317\u5e02\u6c38\u548c\u5340",
"\u53f0\u5317\u5e02\u4e2d\u5c71\u5340","\u53f0\u5317\u5e02\u4e2d\u6b63\u5340",
"\u53f0\u5317\u5e02\u4fe1\u7fa9\u5340","\u53f0\u5317\u5e02\u5357\u6e2f\u5340",
"\u53f0\u5317\u5e02\u5927\u540c\u5340","\u53f0\u5317\u5e02\u5927\u5b89\u5340",
"\u53f0\u5317\u5e02\u6587\u5c71\u5340","\u53f0\u5317\u5e02\u677e\u5c71\u5340",
"\u53f0\u5317\u5e02\u842c\u83ef\u5340"
]
but when I want to convert it to array, I use the code:
data.array
it always give me nil, what can I do?
I've also tried data.arrayValue and data.arrayValue.map {$0.stringValue}
Assume your data construct is
[
{
"data": [
"\u65b0\u5317\u5e02\u4e09\u91cd\u5340",
"\u65b0\u5317\u5e02\u6c38\u548c\u5340",
"\u53f0\u5317\u5e02\u4e2d\u5c71\u5340",
"\u53f0\u5317\u5e02\u4e2d\u6b63\u5340",
"\u53f0\u5317\u5e02\u4fe1\u7fa9\u5340",
"\u53f0\u5317\u5e02\u5357\u6e2f\u5340",
"\u53f0\u5317\u5e02\u5927\u540c\u5340",
"\u53f0\u5317\u5e02\u5927\u5b89\u5340",
"\u53f0\u5317\u5e02\u6587\u5c71\u5340",
"\u53f0\u5317\u5e02\u677e\u5c71\u5340",
"\u53f0\u5317\u5e02\u842c\u83ef\u5340"
]
}
]
Convert JSON into entity meanwhile conform Codable Protocol
typealias DistEntity = [Dist]
struct Dist: Codable {
let data: [String]
}
Implementation model layer
protocol JSONFetcher: AnyObject {
func distParser(forResource fileName: String, completionHandler handler: #escaping((Result<DistEntity, Error>) -> ()))
}
class ModelLayer: JSONFetcher {
enum ParserError: Error {
case PathNotFound
case ConvertsObjectError
case DecoderError
}
func distParser(forResource fileName: String, completionHandler handler: #escaping((Result<DistEntity, Error>) -> ())) {
guard let url = Bundle.main.url(forResource: fileName, withExtension: "json") else { return handler(.failure(ParserError.PathNotFound)) }
guard let jsonData = try? Data(contentsOf: url) else { return handler(.failure(ParserError.ConvertsObjectError)) }
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
guard let distEntity: DistEntity = try? decoder.decode(DistEntity.self, from: jsonData) else { return handler(.failure(ParserError.DecoderError)) }
handler(.success(distEntity))
}
}
Preliminary parse in the interface of business logic layer
final class viewModel {
private var fetcher: JSONFetcher
init(fetcher: JSONFetcher = ModelLayer()) {
self.fetcher = fetcher
}
private func distParser() {
self.fetcher.distParser(forResource: "YourJSONFileName") { (result) in
switch result {
case .success(let entity):
print("[Dist Entity]: \(entity)")
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
Can't make sure that's useful for your scenario,
If no solve, may you should provide more detail info.

How to parse JSON using Codable in Swift?

I am able to parse JSON using JSONSerialization, but unable to parse with Codable.
the json look like this:
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere#april.biz",
}
Please help me with the code.
able to parse using JSONSerialization: data coming
Unable to parse JSON with Codable: data not coming
struct jsonDataModel: Codable{
var name: String
var userName: String
var email: String
init(name: String, username: String, email: String){
self.name = name
self.userName = username
self.email = email
}
}
class WebviewViewController: UIViewController, WKNavigationDelegate {
#IBOutlet weak var testWebView: WKWebView!
//var itemsArray = [jsonDataModel]()
override func viewDidLoad() {
super.viewDidLoad()
serviceCall()
}
func serviceCall()
{
let jsonString = "https://jsonplaceholder.typicode.com/users"
let jsonData = jsonString.data(using: .utf8)!
do {
let jsonDecoder = JSONDecoder()
let user = try jsonDecoder.decode(jsonDataModel.self, from: jsonData)
print("all data \(user)")
print("Hello \(user.name), \(user.userName), \(user.email) ")
} catch {
print("Unexpected error: \(error).")
}
}
}
Please help me to parse json with codable.
Try this example.
import UIKit
import Foundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://jsonplaceholder.typicode.com/users")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
do{
let jsonDataModels = try JSONDecoder().decode([JSONDataModel].self, from: data)
print(String(data: data, encoding: .utf8)!)
print("jsonDataModels: \(jsonDataModels)")
}catch{}
}
task.resume()
}
}
struct JSONDataModel: Codable {
let id: Int
let name, username, email: String
let address: Address
let phone, website: String
let company: Company
}
struct Address: Codable {
let street, suite, city, zipcode: String
let geo: Geo
}
struct Geo: Codable {
let lat, lng: String
}
struct Company: Codable {
let name, catchPhrase, bs: String
}
First of all, if you're using a URL, then to get data you need to use a networking api. URLSession is the iOS provided api to perform network operations like download/upload.
So, just using Codable doesn't make any sense. You need to first have the data in order to parse it with Codable.
Here is the model,
struct Model: Codable {
let id: Int
let name, username, email: String
}
And you can use it in your controller's viewDidLoad() method,
if let url = URL(string: "https://jsonplaceholder.typicode.com/users") {
URLSession.shared.dataTask(with: url) { (data, urlResponse, error) in
if let data = data {
do {
let response = try JSONDecoder().decode([Model].self, from: data)
print(response.map({ $0.name })) //prints all the names
} catch {
print(error)
}
}
}.resume()
}

Alamofire error to decode JSON response with Struct

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
}
}

How should I go about parsing a JSON response from an API with Alamofire and SwiftyJSON?

I am trying to parse a JSON from an API using Alamofire and SwiftyJSON, but am having trouble trying to access the information in the JSON. I need to simply parse the JSON for an item called "ask_price": and also "time_coinapi" but I am not sure how I manage the response, or if I have to use a different method. here is what I have at the moment:
class CoinAPIManager {
var prices: [String] = []
var times: [String] = []
static let shared = CoinAPIManager()
func getReq() {
let headers: HTTPHeaders = [
"X-CoinAPI-Key": "Key"
]
Alamofire.request("https://rest.coinapi.io/v1/quotes/BITSTAMP_SPOT_BTC_USD/history?time_start=2018-08-21T00:00:00&time_end=2018-08-22T00:00:00&limit=100", headers: headers).responseJSON { response in
debugPrint(response)
if let data = try? String(contentsOf: response) {
let json = JSON(parseJSON: data)
parse(json: json)
}
}
func parse(json: JSON) {
for result in json[].arrayValue {
let price = result["ask_price"].stringValue
}
}
}
}
and I have also tried this:
func getReq() {
let headers: HTTPHeaders = [
"X-CoinAPI-Key": "Key"
]
Alamofire.request("https://rest.coinapi.io/v1/quotes/BITSTAMP_SPOT_BTC_USD/history?time_start=2018-08-21T00:00:00&time_end=2018-08-22T00:00:00&limit=100", headers: headers).responseJSON { response in
debugPrint(response)
switch response.result {
case .failure(let error):
// Do whatever here
return
case .success(let data):
// First make sure you got back a dictionary if that's what you expect
guard let json = data as? [String : AnyObject] else {
print("Failed to get expected response from webserver.")
return
}
// Then make sure you get the actual key/value types you expect
guard var price = json["ask_price"] as? Double else {
print("Failed to get data from webserver")
return
}
}
What am I doing wrong? this is how the JSON looks:
[
{
"symbol_id": "BITSTAMP_SPOT_BTC_USD",
"time_exchange": "2013-09-28T22:40:50.0000000Z",
"time_coinapi": "2017-03-18T22:42:21.3763342Z",
"ask_price": 770.000000000,
"ask_size": 3252,
"bid_price": 760,
"bid_size": 124
},
{
"symbol_id": "BITSTAMP_SPOT_BTC_USD",
"time_exchange": "2013-09-28T22:40:50.0000000",
"time_coinapi": "2017-03-18T22:42:21.3763342",
"ask_price": 770.000000000,
"ask_size": 3252,
"bid_price": 760,
"bid_size": 124
}
]
previous question deleted and reposted due to large mistake
you need to change your response to SwiftyJSON object like this
Alamofire.request("https://rest.coinapi.io/v1/quotes/BITSTAMP_SPOT_BTC_USD/history?time_start=2018-08-21T00:00:00&time_end=2018-08-22T00:00:00&limit=100", headers: headers).responseJSON { response in
debugPrint(response)
switch response.result {
case .failure(let error):
// Do whatever here
return
case .success:
// First make sure you got back a dictionary if that's what you expect
let responseJSON = JSON(response.result.value!)
if responseJSON.count != 0 {
print(responseJSON)
//do whatever you want with your object json
}
}
}
i suggest in your ApiManager you can use completion blocks to manage asyncronous request, check the next code.
class func getRequestWithoutParams(didSuccess:#escaping (_ message: JSON) -> Void, didFail: #escaping (_ alert:UIAlertController)->Void){
Alamofire.request("http://foo.bar"),method: .post,parameters: parameters,encoding: JSONEncoding.default,headers:nil).responseJSON { response in
switch response.result{
case .success:
let res = JSON(response.result.value!)
didSuccess(res)
break
case .failure(let error):
let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
let done = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(done)
didFail(alert)
}
}
}
From Swift 4, you should be able to use codable to solve it:
struct YourStructure: Codable {
let symbol_id: String?
let time_exchange: String?
let ask_price: String?
private enum CodingKeys: String, CodingKey {
case symbol_id = "symbol_id"
case time_exchange = "time_exchange"
case ask_price = "ask_price"
}
}
And then parse it with JSONDecoder
let decoder = JSONDecoder()
let parsedData = decoder.decode(YourStructure.self, from: "YourJsonData")