How to convert class to decodable Json like #SerializedName in Swift? - swift

I need create a two class to decode json response, like #SerializedName in Kotlin, like this:
class PixHistoryResponse(
#SerializedName("cadastro")
var createdAt: String = "",
#SerializedName("status")
var status: String = "",
#SerializedName("valor")
var finalAmount: String = "",
#SerializedName("timeline")
var history: MutableList<PixTimelineResponse> = mutableListOf(),
#Keep
class PixTimelineResponse(
#SerializedName("cadastro")
var date: String = "",
#SerializedName("status")
var event: String = "",

From my experience, I think you should use struct for modeling data, because it's a value type. Should not use reference type like class.
struct PixHistoryResponse: Decodable {
var createdAt: String = ""
var finalAmount: String = "",
var history: [PixTimelineResponse] = []
enum CodingKeys: String, CodingKey {
case createdAt = "cadastro"
case finalAmount = "valor"
case hisotry = "timeline"
}
}
struct PixTimelineResponse: Decodable {
var date: String = ""
var event: String = ""
enum CodingKeys: String, CodingKey {
case data = "cadastro"
case event = "status"
}
}

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]?

Swift ui codable struct with empty fields

I have the following structs:
struct ResponseToken: Identifiable, Codable, Hashable {
var id: UUID { return UUID() }
let access_token: String
enum CodingKeys: String, CodingKey {
case access_token
}
}
struct ResponseUserInfo: Identifiable, Codable, Hashable {
var id: UUID { return UUID() }
let login, avatar_url, html_url, created_at, updated_at: String
let public_repos, public_gists, followers, following: Int
enum CodingKeys: String, CodingKey {
case login, avatar_url, html_url, public_repos, public_gists, followers, following, created_at, updated_at
}
}
I would like to avoid doing this every time to declare empty objs:
var token: ResponseToken = ResponseToken(access_token: "")
var userInfo: ResponseUserInfo =
ResponseUserInfo(login: "", avatar_url: "", html_url: "", created_at: "", updated_at: "", public_repos: 0, public_gists: 0, followers: 0, following: 0)
The result I would like to have is something like this:
var token: ResponseToken = ResponseToken()
var userInfo: ResponseUserInfo = ResponseUserInfo()
Can you give me a hand?
A possible solution is a static property which creates an empty struct for example
struct ResponseToken: Identifiable, Codable, Hashable {
let id = UUID()
let accessToken: String
enum CodingKeys: String, CodingKey {
case accessToken = "access_token"
}
static let empty = ResponseToken(accessToken: "")
}
and use it
let token = ResponseToken.empty
Notes:
The computed property to return an UUID is pointless, declare a constant.
If you are specifying CodingKeys anyway, use them also to convert the snake_case keys.

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:

Using models with sub models in CoreData

I am trying to implement a cache using CoreData.
Up until this point I've been storing models that are simple, however I have a model below that contains data types such as CodablePartialUser and CodableFeedItemType.
How should these types be modelled in CoreData?
Should I use the Data type and store them in a data format?
As CodableFeedItemType is an enum, should I store the raw value and convert between formats again?
struct CodablePartialUser: Equatable, Codable {
let userID: String
let firstName: String
let lastName: String
init(userID: String, firstName: String, lastName: String) {
self.userID = userID
self.firstName = firstName
self.lastName = lastName
}
}
enum CodableFeedItemType: String, Codable {
case recognition = "RECOGNITION"
case news = "COMPANY_NEWS"
}
struct CodableFeedItem: Codable {
let id: String
let type: CodableFeedItemType
let createdDate: Date
let createdBy: CodablePartialUser
let likesCount: Int
let commentsCount: Int
let externalID: String
let title: String?
let imageURL: URL?
init(id: String, type: CodableFeedItemType, createdDate: Date, createdBy: CodablePartialUser, likesCount: Int, commentsCount: Int, externalID: String, title: String?, imageURL: URL?) {
self.id = id
self.type = type
self.createdDate = createdDate
self.createdBy = createdBy
self.likesCount = likesCount
self.commentsCount = commentsCount
self.externalID = externalID
self.title = title
self.imageURL = imageURL
}
}
For the CodablePartialUser you can use relationship by creating a new Entity named as "CodablePartialUser"
For CodableFeedItemType you can use enum as like
enum CodableFeedItemType: String, Codable {
case recognition = "RECOGNITION"
case news = "COMPANY_NEWS"
}
extension CodableFeedItemEntity {
var type: CodableFeedItemType {
get {
return CodableFeedItemType(rawValue: typeRaw)!
}
set {
typeRaw = newValue.rawValue
}
}
}

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