简体   繁体   English

如何在 Swift 中的一个数组内声明不同的结构

[英]How to declare different structs inside one array in Swift

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.理想情况下,该数组将始终根据运动type仅保存一种运动类型。

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.最简单的方法是将其存在不确定的属性声明为可选的,并创建一个组合两个可选属性的通用struct 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.由于我们已经有一个MovementType可以确定特定属性的可用性,我们可能可以强制解包,尽管安全解包在任何时候都更安全。

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.这并不完全符合您的描述,因为结构上有多种数组类型,但对于对象的使用者,您可以访问单个属性workouts ,它将返回多种可能类型中的一种。

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).但是,正如 Tushar 在他的评论中指出的那样,在两个不同的地方( type和数组中返回的实际类型)指定返回类型是很脆弱的。 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在这种情况下,我建议手动解码 JSON 并使用关联类型声明类型枚举

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.显式的 UUID 分配意味着id不会被解码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM