#swift #struct
#swift #структура
Вопрос:
Возможно ли создать массив из разных структур? Моя структура данных выглядит следующим образом:
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
}
Краткий пример того, что должно делать:
Пользователь может создавать несколько движений с именем и типом движения. Пользователь может добавлять тренировки к движению, но поскольку каждый movementType содержит разные данные, я создаю для этого разные структуры.
в идеале массив всегда будет содержать только один тип движения, основанный на type
движении.
Комментарии:
1. Вы можете сделать так, чтобы ваши структуры модели подтверждались базовым типом, могли быть пустым классом или протоколом, основанным на ваших требованиях. Создайте массив базового типа и добавляйте объекты, используя композицию, вместо того, чтобы передавать каждый тип по отдельности, что само по себе неверно.
Ответ №1:
Самым простым методом было бы объявить свойства, присутствие которых неизвестно, как необязательные, и создать общее struct
, объединяющее оба необязательных свойства. Поскольку у нас уже есть a MovementType
, который обеспечивал бы уверенность в доступности определенного свойства, мы, вероятно, могли бы принудительно развернуть, хотя safe-unwrap безопаснее в любой момент.
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
}
Ответ №2:
Я бы сделал это так:
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
}
}
}
Это не совсем то, что вы описываете, поскольку в структуре существует несколько типов массивов, но для потребителя объекта у вас есть доступ к одному свойству workouts
, которое возвращает один из нескольких возможных типов.
Однако, как указывает Тушар в своем комментарии, сложно указать, какой тип возвращаемого значения находится в двух разных местах ( type
и фактический тип, возвращаемый в массиве). Подкласс или протокол, вероятно, были бы лучше.
Ответ №3:
в идеале массив всегда будет содержать только один тип движения в зависимости от типа движения.
В этом случае я рекомендую декодировать JSON вручную и объявить тип enum со связанными типами
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
}
Явное присвоение UUID подразумевает, что id
это не будет декодироваться.