Codable Decodable JSON from String to Enum - swift

I'm trying to decode a JSON using codable. I'm wondering if there's a way to customize codable to return HelloModel's typeCustomer as type TypeOfCustomerEnum instead of String?
Example:
{
"name": "Hello",
"lastName": "World",
"typeOfCustomer": "Student"
}
enum TypeOfCustomerEnum: String {
let Student = "Student"
let Paying = "Paying"
let NonPaying = "Nonpaying"
}
struct HelloModel: Codable {
let name: String
let lastName: String
let typeOfCustomer: TypeOfCustomerEnum // JSON for TypeOfCustomer is a String but TypeOfCustomer wanted
}

The type TypeOfCustomerEnum must also conform to Codable and the cases (must be cases) should be lowercased and the literal strings must match the JSON values
enum TypeOfCustomerEnum: String, Codable {
case student = "Student"
case paying = "Paying"
case nonPaying = "NonPaying"
}
struct HelloModel: Codable {
let name: String
let lastName: String
let typeOfCustomer: TypeOfCustomerEnum
}

Related

Struct with array for Alamofire responseDecodable

I'm struggling to setup a struct correctly for Alamofire responseDecodable to be decoded.
My JSON return structure is:
{
“SESSIONID” : ”GUID”,
“ISADMIN” : ”YES or NO”,
“FNAME” : ”ABC”,
“SNAME” : ”ABC”,
“EMPNO” : ”ABC”,
"SITES": [
{
"NAME": “MTN-ALICE LANE”,
"WEBSITEAPPID": “SiteGUID”
}
]
}
My Swift code is where the issue is under the array SITES. I know this because if I remove the SITES from my struct then the rest of the JSON response accordingly but without the SITES array.
struct ValidateUser: Decodable{
let sessionId: String?
let isAdmin: String?
let fullName: String?
let surname: String?
let employNo: String?
let siteNames: [UserSites]
enum CodingKeys: String, CodingKey {
case sessionId = "SESSIONID"
case isAdmin = "ISADMIN"
case fullName = "FNAME"
case surname = "SNAME"
case employNo = "EMPNO"
case siteNames = "SITES"
}
}
struct UserSites: Decodable{
let siteName: String?
let siteId: String?
enum CodingKeys: String, CodingKey {
case siteName = "NAME"
case siteId = "WEBSITEAPPID"
}
}
I assume my error is how I've created struct UserSites but unsure.
Missing option ? after let siteNames: [UserSites]
let siteNames: [UserSites]?

Type does not conform to protocol 'Decodable' or "Encodable"

Im fairly new to programming and I fixed a few errors but only one error ist left, which states: Type 'DataClass' does not conform to protocol 'Decodable' and Type 'DataClass' does not conform to protocol 'Encodable' on line 14.
I checked smiliar questions but I'm not fairly sure if i can find a solution if i put init
// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
// let welcome = try? newJSONDecoder().decode(Welcome.self, from: jsonData)
import Foundation
// MARK: - Welcome
struct Welcome1: Codable {
let data: DataClass
}
// MARK: - DataClass
struct DataClass: Codable {
let id: Int
let entityType, label: String
let apiURL, abgeordnetenwatchURL: String
let firstName, lastName: String
let birthName: String
let sex: String
let yearOfBirth: Int
// let party: Referenz -> Party???
let partyPast: String
let education: String
let residence: String
let occupation: String
let statisticQuestions: Int
let statisticQuestionsAnswered: Int
let qidWikidata, fieldTitle: String
enum CodingKeys: String, CodingKey {
case id = "id"
case entityType = "entity_type"
case label = "label"
case apiURL = "api_url"
case abgeordnetenwatchURL = "abgeordnetenwatch_url"
case firstName = "first_name"
case lastName = "last_name"
case birthName = "birth_name"
case sex = "sex"
case yearOfBirth = "year_of_birth"
case party = "party"
case partyPast = "party_past"
case education = "education"
case residence = "residence"
case occupation = "occupation"
case statisticQuestions = "statistic_questions"
case statisticQuestionsAnswered = "statistic_questions_answered"
case qidWikidata = "qid_wikidata"
case fieldTitle = "field_title"
}
}
// MARK: - Party
struct Party1: Codable {
let id: Int
let entityType, label: String
let apiURL: String
enum CodingKeys: String, CodingKey {
case id
case entityType = "entity_type"
case label
case apiURL = "api_url"
}
}
I am trying to use Json parser to collect data from this site lingk. I used quicktype recommended by a tutorial.
Remove
case party = "party" line
or open
// let party: Referenz -> Party? line

Exclude CodingKeys that doesn't need to be altered?

Say I have a struct User model which has many properties in it.
struct User: Codable {
let firstName: String
let lastName: String
// many more properties...
}
As you can see above it conforms to Codable. Imagine if the lastName property is should be encoded/decoded as secondName and I would like to keep it as lastName at my end, I need to add the CodingKeys to the User model.
struct User: Codable {
//...
private enum CodingKeys: String, CodingKey {
case firstName
case lastName = "secondName"
// all the other cases...
}
}
Is there any possible way to avoid including all the cases in CodingKeys that have the same value as rawValue like the firstName in the above example (Feels redundant)? I know if I avoid the cases in CodingKeys it won't be included while decoding/encoding. But, is there a way I could override this behaviour?
There is a codable way, but the benefit is questionable.
Create a generic CodingKey
struct AnyKey: CodingKey {
var stringValue: String
var intValue: Int?
init?(stringValue: String) { self.stringValue = stringValue; self.intValue = nil }
init?(intValue: Int) { self.stringValue = String(intValue); self.intValue = intValue }
}
and add a custom keyDecodingStrategy
struct User: Codable {
let firstName: String
let lastName: String
let age : Int
}
let jsonString = """
{"firstName":"John", "secondName":"Doe", "age": 30}
"""
let data = Data(jsonString.utf8)
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom({ keyPath -> CodingKey in
let key = keyPath.last!
return key.stringValue == "secondName" ? AnyKey(stringValue:"lastName")! : key
})
let result = try decoder.decode(User.self, from: data)
print(result)
} catch {
print(error)
}
There is not such a feature at this time. But you can take advantage of using computed properties and make the original one private.
struct User: Codable {
var firstName: String
private var secondName: String
var lastName: String {
get { secondName }
set { secondName = newValue }
}
}
So no need to manual implementing of CodingKeys at all and it acts exactly like the way you like. Take a look at their counterparts:

Swift Dynamically choose Codable struct for JSON field value based on response type

I'm using a codable struct to handle an API JSON response. The data structure in one of the fields (slideData_ varies depending on the type of card the response relates to (cardType).
I therefore need to make a simple conditional in the struct that sets the value of slideData to be either ApiCompanyProfileCardData or ApiPersonalProfileCardData based on the value returned in the cardType field. I've been searching for quite a while and I believe enum and CodingKeys are probably the way to go but I'm stuck trying to implement this. It feels as though it should be fairly straightforward. Can somebody please help?
{"card":
[
{"cardTitle": "Title 1",
"cardType": "companyprofile",
"cardTypeDesc": "Company profile",
"id": 10,
"imageFile": "b443709ca8d8a269ca185381bfa2ad8326af33c3.png",
"slideData": {"cp_name": "Full Name",
"cp_name_font_style": "'Nunito', sans-serif",
"cp_name_font_weight": "900",
"cp_name_size_quantity": "30px",
"cp_name_text_color": "ff0000",
"cp_title": "CEO",
"cp_title_font_style": "Arial",
"cp_title_font_weight": "normal",
"cp_title_size_quantity": "20px",
"cp_title_text_color": "000000"}
}
]
}
struct ApiCardData: Codable {
let card: [Card]
struct Card : Codable {
let cardTitle: String
let cardType: String
let id: Int
let imageFile: String
let slideData: ApiCompanyProfileCardData
}
struct ApiCompanyProfileCardData: Codable {
let cpName: String
let cpNameFontStyle: String
let cpNameFontWeight: String
let cpNameSizeQuantity: String
let cpNameTextColor: String
let cpTitle: String
let cpTitleFontStyle: String
let cpTitleFontWeight: String
let cpTitleSizeQuantity: String
let cpTitleTextColor: String
}
struct ApiPersonalProfileCardData: Codable {
let ppEmail: String
let ppEmailFontStyle: String
let ppEmailFontWeight: String
let ppEmailSizeQuantity: String
let ppEmailTextColor: String
let ppName: String
let ppNameFontStyle: String
let ppNameFontWeight: String
let ppNameSizeQuantity: String
let ppNameTextColor: String
}
}
#N.Widds Then you can form your struct like below, Hope it helps you.
struct ApiCardData: Codable {
let card: [Card]
struct Card : Codable {
let cardTitle: String
let cardType: String
let id: Int
let imageFile: String
let slideData: ApiCompanyProfileCardData?
let slideApiPersonalProfileData: ApiPersonalProfileCardData?
}
struct ApiCompanyProfileCardData: Codable {
let cpName: String
let cpNameFontStyle: String
enum CodingKeys: String, CodingKey {
case cpName = "cp_name"
case cpNameFontStyle = "cp_name_font_style"
}
}
struct ApiPersonalProfileCardData: Codable {
let ppEmail: String
let ppEmailFontStyle: String
enum CodingKeys: String, CodingKey {
case ppEmail = "pp_Email"
case ppEmailFontStyle = "pp_email_font_style"
}
}
}

Decodable JSONSerialization error custom object alamofire

I have a custom APIClient using alamofire5 beta that conforms to Codable protocol for the request.
I'm trying to send a custom object via httpBody (post) and I'm getting this error:
Invalid type in JSON write (_SwiftValue)
This is the object that I'm trying to send:
struct Complex: Codable {
var id: String
var name: String
var address: String
var zipcode: String
var amenities: [String]
var schedules: [ComplexSchedules]
init(id: String, name: String, address: String, zipcode: String, amenities: [String], schedules: [ComplexSchedules]) {
self.id = id
self.name = name
self.address = address
self.zipcode = zipcode
self.amenities = amenities
self.schedules = schedules
}
enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
case address = "address"
case zipcode = "zipcode"
case amenities = "amenities"
case schedules = "schedules"
}
struct TimeRange: Codable {
var from: String
var to: String
init(from: String, to: String) {
self.from = from
self.to = to
}
enum CodingKeys: String, CodingKey {
case from = "from"
case to = "to"
}
}
struct ComplexSchedules: Codable {
var day: String
var timeRanges: [TimeRange]
init(day: String, timeRanges: [TimeRange]) {
self.day = day
self.timeRanges = timeRanges
}
enum CodingKeys: String, CodingKey {
case day = "day"
case timeRanges = "time_ranges"
}
}
}
It fails when I call this method:
urlRequest.httpBody = try JSONSerialization.data(withJSONObject: complex, options: [])
Any thoughts?
You may need
do {
let data = try JSONEncoder().encode(complex)
urlRequest.httpBody = data
}
catch {
print(error)
}
as Codable is used to be able to utilize JSONDecoder and JSONEncoder not to use with JSONSerialization that expects a non-custom object like raw String/Array/Dictionary