Decode Json Data using JsonDecoder and Alamofire - swift

I am trying decode Json data into my Model.
This is my model
struct Devices : Codable {
var id :String?
var description :String?
var status : Int?
}
var heroes = Devices()
print(DeviceId)
let loginParam: [String: Any] = [
"id": DeviceId
]
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 5
manager.request("http://13.13.13.004/website/api/Customer/DeviceControl", method: .post , parameters: loginParam, encoding: JSONEncoding.prettyPrinted)
.responseData { response in
let json = response.data
do{
let decoder = JSONDecoder()
//using the array to put values
heroes = try decoder.decode(Devices.self, from: json!)
}catch let err{
print(err)
}
this code doesn't get in catch block.
But heroes values return nill.
When ı try use NsDictionary
Its give result.

This is a common mistake: You forget the root object
struct Root : Decodable {
private enum CodingKeys: String, CodingKey { case resultCount, devices = "results" }
let resultCount : Int
let devices : [Device]
}
And name the device struct in singular form (devices is an array of Device instances) and declare the members as non-optional
struct Device : Decodable {
var id : String
var description : String
var status : Int
}
...
var heroes = [Device]()
...
let result = try decoder.decode(Root.self, from: json!)
heroes = result.devices

Related

Getting an array of objects using FirebaseFirestoreSwift

I have a struct that looks like this:
struct Joint: Identifiable, Codable {
#DocumentID var id : String? = UUID().uuidString
var serviceType : Int
var businessName : String
var keywords: [String]
var menu : [DishItems]
enum CodingKeys : String, CodingKey {
case businessName
case serviceType = "jointType"
case keywords
case menu
}
}
struct DishItems: Codable {
var listOrder : Int
var menuItemName : String
var menuItemDescription : String
var menuItemPrice : Double
}
The DishItems is a custom object and I am using FirebaseFireStoreSwift JSON decoder to retrieve the documents.
let db = Firestore.firestore().collection("Joints")
db.getDocuments { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No restaurants")
return
}
let jointsArray : [Joint] = documents.compactMap {
return try? $0.data(as: Joint.self)
}
completion(jointsArray)
}
I am able to query all of the documents if I exclude "menu" from the parent struct but as soon as I include it, then nothing is queried. How do I go about obtaining the array of objects inside the JSON?

How to store and retrieve variable with custom type

I have a variable identity with type ETIdentity that I need to store in ViewController1 and retrieve in ViewController2,
ViewController1
//Variables
var activationCodeFromCore, serialNumberFromCore, entityNameFromCore, deviceIdFromCore, registrationCodeFromCore, entityFromCore: String?
var activationCode, serialNumber, entityName, deviceId, registrationCode, entity: String?
var counter: Int = 0
var storedIdentity: ETIdentity?
Below is the storedIdentity that I need to keep
let storedIdentity = BridgeSDKUtils.performClassicActivation("26586-05858", withActivationCode: "8998-6857-1357-1870", "entidad0");
GlobalIdentity.identity = storedIdentity;
func softTokenDataService() {
let storedIdentity = BridgeSDKUtils.performClassicActivation("26586-05858", withActivationCode: "8998-6857-1357-1870", "entidad0");
GlobalIdentity.identity = storedIdentity;
self.activationCode = "8998-6857-1357-1870"
self.serialNumber = "26586-05858"
self.entityName = "entityData\(counter)"
self.deviceId = "\(String(describing: storedIdentity?.deviceId))"
self.registrationCode = "\(String(describing: storedIdentity?.registrationCode))"
self.entity = "\(storedIdentity!)"
}
...
func getEntityCore()
{
//Variables that are going to be stored
self.activationCodeFromCore = activationCode
self.serialNumberFromCore = serialNumber
self.entityNameFromCore = entityName
self.deviceIdFromCore = deviceId
self.registrationCodeFromCore = registrationCode
self.entityFromCore = storedIdentity
}
...
//SecureStorage Function
func saveEntityToCoreData()-> Bool {
self.softTokenDataService()
var SavedItem:Bool = true
var arr : [[String: Any]] = [[
"activationCode": self.activationCodeFromCore,
"serialNumber": self.serialNumberFromCore,
"entityName": self.entityNameFromCore,
"deviceId": self.deviceIdFromCore,
"registrationCode": self.registrationCodeFromCore,
"entity": self.entityFromCore]]
let jsonData = try! JSONSerialization.data(withJSONObject: arr, options: [.prettyPrinted])
let json = String(data: jsonData, encoding: String.Encoding.utf8)!
if self.saveRutSwitchOn
{
SecureData.save(key: "entityData0)", data: json.data(using: .utf8)!)
}
SavedItem = self.saveRutSwitchOn
return SavedItem
}
ViewController2
struct Person {
var activationCode: String
var serialNumber: String
var entityName: String
var deviceId: String
var registrationCode: String
var entity: String
}
struct EntityModel: Codable {
let activationCode, serialNumber, entityName, deviceId, registrationCode, entity: String?
}
if let loadedData = SecureData.load(key: "entityData0") {
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let entityData = try decoder.decode([EntityModel].self, from: loadedData)
entityData.forEach { (EntityModel) in
//Here I Imagine something like this
//var identity: ETIdentity?
//identity = EntityModel.entity
////Here I have the identity, so I can manipulate it like needed, because is from type ETIdentity I can access its methods.
//identity?.getOTP(Date())
}
} catch {
print(error)
}
}
Reference Data:
GlobalIdentity.swift
struct GlobalIdentity{
static var identity : ETIdentity?
}
ETIdentity.h
#interface ETIdentity : NSObject<NSCoding> {
#private
-(NSString*)getOTP:(NSDate*)time;
#end
EDIT
The problem is that the variable entity (where I need to call its parameters in ViewController2), is not a String, so it crashes, it doesn't work. I also tried to put the variable identity with the type I needed var identity: ETIdentity?, but ETIdentity isn't in protocol with Codable (to work with struct so I can call them in ViewController2)
I read your question and found basic thing that you are missing is your entity seems to be another model like a dictionary or some other type if it is dictionary then do this. Currently your SecureData.load(key: "entityData0") contain all the data that return array of entity models, And your Data object loadedData must contain entity object, Why not you try to implement Codable for your entity like this.
struct SortedArryModel: Codable {
var sorterarrkey: String? // if your array contain strings
}
And use this SortedArryModel in your EntityModel
after that you can get your array like [key:string], just convert that string to array.
Suggestion: in your case better to use JsonSerialization instead of codable, if you still want to apply codable then your elements should conform codable protocol. Like I mentioned above, implement your entity as model that confrom codable protocol.

Decoding structure use by SwiftyJson and Alamofire

How can I decode data to a structure using Alamofire and SwiftyJSON? My attempts give me errors like that
"No value associated with key CodingKeys(stringValue: \"user\",
intValue: nil)
Here is my code, my try doesn't give me the result when I use non-optional values, they respond to me with NIL values
Alamofire.request(url, method: .post, parameters: params, encoding: URLEncoding.default, headers: nil).responseJSON { (response) in
if response.data != nil {
switch response.result {
case.failure( let error):
print(error)
case.success(let val):
var json = JSON(val)
print(json)
guard let data = response.data else {return}
do {
let root = try JSONDecoder().decode(MainInfo.self, from: data)
print(root.submodel)
}
catch {
print("Bigerror")
print(error)
}
This is my structure
struct user: Codable {
var push_id:String?
var name:String?
var id:String?
var role_id:String?
var taxi_park_id:Int?
var car_number:String?
enum CodingKeys:String,CodingKey {
case push_id = "push_id"
case name = "name"
case id = "id"
case role_id = "role_id"
case taxi_park_id = "taxi_park_id"
case car_number = "car_number"
}
}
struct MainInfo : Decodable {
let model: String?
let submodel: String?
let user:user
enum CodingKeys:String,CodingKey {
case model = "model"
case submodel = "submodel"
case user = "user"
}
}
This is my pretty printed json
{
"facilities" : [
],
"model" : "AMC",
"taxi_park" : "Taxi +",
"submodel" : "Gremlin",
"user" : {
"role_id" : 2,
"push_id" : "dW7Cy-ItcDo:APA91bH62zJJKKz0t9VxP29H0iE2xhnQH0hDvKpGaHc5pknuTuZq2lMaj-EapQlN3O4dJF0ysSuCNOeb-2SdJaJaLIZcwHD3CCpeNpz6UVeGktoCm2ykL2rNXF5-ofQckvz1xTvVO0V6",
"taxi_park_id" : 0,
"id" : 3,
"name" : "China",
"car_number" : "X123OOO"
}
}
First of all your question has nothing to do with SwiftyJSON as you are using Codable.
Second of all name the structs with starting capital letter (User), that avoids confusion like let user : user
The error is misleading. All .._id values except push_id are Int rather than String. It's very easy to distinguish strings from all other types: Strings are always wrapped in double quotes.
And if you pass the convertFromSnakeCase key decoding strategy you don't need CodingKeys at all
struct MainInfo : Decodable {
let model : String
let submodel : String
let user : User
}
struct User: Decodable {
let pushId : String
let name : String
let id : Int
let roleId : Int
let taxiParkId : Int
let carNumber : String
}
...
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let root = try decoder.decode(MainInfo.self, from: data)
print(root.submodel)
} catch { print(error) }
Try this code, also a simple tip, we use coding keys in swift because sometimes we have to receive an inconvenient parameter keys but we also want to use it simple and clearly in the struct therefore CodingKeys are helpful in your case you using CodingKeys to decode the same parameter name i added the following taxiPark propriety to give you a hint on why they are useful, for example: i want to parse a JSON that have a key called
Person_ID_From_School
with coding keys i can do that with a better naming simple as personId and so on
struct MainInfo : Decodable {
let model: String?
let submodel: String?
let user:user
let taxiPark: String?
let facilities: [String?]?
enum CodingKeys:String,CodingKey {
case model = "model"
case submodel = "submodel"
case user = "user"
case taxiPark = "taxi_park"
case facilities = "facilities"
}
}

Convert an Int JSON value to String enum case

Here's an simplified version of the class:
class Movie: Codable {
var name: String
var genre: MovieGenre
init(name: String, genre: MovieGenre) {
self.name = name
self.genre = genre
}
}
enum MovieGenre: String, Codable {
case action
case drama
case horror
}
And the JSON:
{
"name" : "Test",
"genre" : 1
}
I know the relation between the JSON genre value and the MovieGenre enum is:
1 = action
2 = drama
3 = horror
Using JSONDecoder, how can I convert the JSON genre Int value to my enum MovieGenre?
I would like not to have to write an init from decoder, because it would be very verbose having to convert each attribute manually.
Here's an example:
let movie = Movie(name: "Test", genre: .action)
let jsonEncoder = JSONEncoder()
let jsonDecoder = JSONDecoder()
do {
// encoding
let jsonData = try jsonEncoder.encode(movie)
let jsonString = String(data: jsonData, encoding: .utf8)
print("JSON String : " + jsonString!) // prints: JSON String : {"name":"Test","genre":"action"}
// decoding
let json = "{\"name\":\"Test\",\"genre\":1}".data(using: .utf8)!
_ = try jsonDecoder.decode(Movie.self, from: json)
} catch {
print(error) // prints: typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "genre", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))
}
Your type for your enum does not match with the type in the JSON.
I changed the Type for your enum to Int and set the initial value for action (as the default would be 0) and got the expected result without custom decoding.
Trying this in a playground:
import UIKit
import PlaygroundSupport
let jsonData = """
{
"name" : "Test",
"genre" : 1
}
""".data(using: .utf8)!
class Movie: Codable {
var name: String
var genre: MovieGenre
init(name: String, genre: MovieGenre) {
self.name = name
self.genre = genre
}
}
enum MovieGenre: Int, Codable {
case action = 1
case drama
case horror
}
let decoder = JSONDecoder()
let result = try? decoder.decode(Movie.self, from: jsonData)
print(result?.name)
print(result?.genre)
print(result?.genre.rawValue)
Output is:
Optional("Test")
Optional(__lldb_expr_39.MovieGenre.action)
Optional(1)
This should also encode in the same way.

How to convert Dictionary to JSON in Vapor 3?

In Vapor 1.5 I used to convert Dictionary to JSON as shown below.
How should I do it in Vapor 3.1?
In docs it says I need to create struct type and conform it to codable protocol.
Is there another method that would enable to convert the existing dictionary without creating new struct?
func makeCustomJSON(clientData: DataFromClientChargeWithCard, paymentID:String,customer: String) throws -> JSON {
var dictionaryOfStrings = [String:String]()
dictionaryOfStrings["BookingState"] = "Active"
dictionaryOfStrings["BookingStateTimeStamp"] = clientData.TimeStampBookingSavedInDB
dictionaryOfStrings["ChangesMadeBy"] = "User"
dictionaryOfStrings["PaymentID"] = paymentID
dictionaryOfStrings["DateAndTime"] = clientData.DateAndTime
dictionaryOfStrings["RatePriceClient"] = clientData.RatePriceClient
dictionaryOfStrings["Locality"] = clientData.Locality
dictionaryOfStrings["StripeCustomerID"] = customer
//some more properties below...
let response:JSON
do {
response = try JSON(node: dictionaryOfStrings)
}catch let error as NSError {
let message = "dictionaryOfStrings can't be converted in JSON"
drop.log.error(message)
logErr.prints(message: message, code: error.code)
throw NSError(domain: "errorDictionary", code: 400, userInfo: ["error" : error.localizedDescription])
}
return response
}
If you have a type like the struct that I have defined here you can simply return it, as long as it conforms to Content.
import Vapor
import FluentSQLite
struct Employee: Codable {
var id: Int?
var name: String
init(name:String) {
self.name = name
}
}
extension Employee: SQLiteModel {}
extension Employee: Migration {}
extension Employee: Parameter {}
extension Employee: Content { }
and then you can simply define a route and return it.
router.get("test") { req in
return Employee(name: "Alan")
}
if you want to use dictionary you can use the JSONSerialization and return a string.
router.get("test2") { req -> String in
let employeeDic: [String : Any] = ["name":"Alan", "age":27]
do {
let data = try JSONSerialization.data(withJSONObject: employeeDic, options: .prettyPrinted)
let jsonString = String(data: data, encoding: .utf8)
return jsonString ?? "FAILED"
}catch{
return "ERROR"
}
}