I want to clear my structure.
I want to reset all the values to nil.
How do i do this???
enum HealthDataType: String, Codable {
case heartRate
case bmi
}
struct HealthDataItem: Codable {
let endDate: String
let value: String
let startDate: String
let type: HealthDataType
}
Related
I have the following ClassRoom Struct
struct ClassRoom: Codable {
let name: String
let classId: String
let date: String
}
While I am forming a ClassRoom object, I do not need date object.
// do not need date object here!,
let classRoom = ClassRoom(name: "Math", classId: "12343")
Missing argument for parameter 'date' in call
But I need date object when I decode the ClassRoom.
You need to make your date be optional.
Two approaches:
You can keep using the compiler synthesized memberwise initializer. For it to use nil as a default value, you need to make the date variable mutable:
struct ClassRoom: Codable {
let name: String
let classId: String
var date: String? = nil
}
If you want to keep it mutable, you'll have to add your own initializer, with a defaulted parameter, like so:
struct ClassRoom: Codable {
let name: String
let classId: String
let date: String?
init(name: String, classId: String, date: String? = nil) {
self.name = name
self.classId = classId
self.date = date
}
}
Is it possible to create an array of different structs?
My data structure looks like this:
enum MovementType: String, Codable {
case WeightMovement
case RepsMovement
}
struct Movement: Identifiable, Codable {
let id: UUID = UUID()
let name: String
let type: MovementType
let workouts: [WeightMovement, RepsMovement] ---> A solution for this line based on the Type above
}
struct WeightMovement: Identifiable, Codable {
let id: UUID = UUID()
let weight: Double
let sets: Int
let reps: Int
}
struct RepsMovement: Identifiable, Codable {
let id: UUID = UUID()
let sets: Int
let reps: Int
let seconds: Int
}
A brief example of what is should do:
A user can create multiple movements with a name and movementType. A user can add workouts to a movement but since each movementType holds different data i create different structs for that.
ideally the array will always hold only one type of Movement based on the type of the movement.
The easiest method would be to declare the properties whose presence is uncertain as optional and create a common struct that combines both of the optional properties. Since we already have a MovementType that would provide certainty about the availability of a particular property we could probably force-unwrap, although safe-unwrap is safer at any point.
struct Movement: Identifiable, Codable {
var id: UUID = UUID()
let name: String
let type: MovementType
let workouts: [MovementDetail]
}
struct MovementDetail: Identifiable, Codable {
var id: UUID = UUID()
let weight: Double?
let sets: Int
let reps: Int
let seconds: Int?
}
enum MovementType: String, Codable {
case WeightMovement
case RepsMovement
}
I would do it this way:
struct Movement: Identifiable, Codable {
var id: UUID = UUID()
var name: String
var type: MovementType
var weightWorkouts: [WeightMovement]
var repsWorkouts: [RepsMovement]
var workouts: [Codable] {
switch type {
case .WeightMovement:
return weightWorkouts
case .RepsMovement:
return repsWorkouts
}
}
}
This doesn't do exactly what you describe, as there are multiple array types on the struct, but for the object's consumer, you have access to a single property workouts that will return one of multiple possible types.
As Tushar points out in his comment, though, it is fragile to specify what the return type is in two different places (type and the actual type returned in the array). A subclass or protocol would probably be better.
ideally the array will always hold only one type of Movement based on the type of the movement.
In this case I recommend to decode the JSON manually and declare the type enum with associated types
enum MovementType {
case weight([WeightMovement])
case reps([RepsMovement])
}
struct Movement: Identifiable, Decodable {
private enum CodingKeys : String, CodingKey { case name, type, workouts }
let id: UUID = UUID()
let name: String
let type: MovementType
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
let movementType = try container.decode(String.self, forKey: .type)
switch movementType {
case "WeightMovement":
let workouts = try container.decode([WeightMovement].self, forKey: .workouts)
type = .weight(workouts)
case "RepsMovement":
let workouts = try container.decode([RepsMovement].self, forKey: .workouts)
type = .reps(workouts)
default: throw DecodingError.dataCorruptedError(forKey: .type, in: container, debugDescription: "Invalid movement type")
}
}
}
struct WeightMovement: Identifiable, Decodable {
let id: UUID = UUID()
let weight: Double
let sets: Int
let reps: Int
}
struct RepsMovement: Identifiable, Decodable {
let id: UUID = UUID()
let sets: Int
let reps: Int
let seconds: Int
}
The explicit UUID assignment implies that id is not going to be decoded.
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:
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"
}
}
}
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
}