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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.