You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Worksheet结构体设计继承式数据模型以复用视图与函数?

高效复用Worksheet类型的实现方案需求

我有多种Worksheet结构体,用于填充视图供用户填写内容后保存。此前单独使用每个结构体,现在希望设计一套包含**公共属性(id、date)**的抽象方案,复用视图与函数,避免为每种Worksheet单独创建重复代码。

原本读取某类Worksheet数据的代码如下:

if let diaryArray = vm.readData(of: [Diary](), from: "diary_data") {
    let sortedArray = diaryArray.sorted(by: {$0.date > $1.date})
    vm.diaries = sortedArray
}

我希望将其改写为泛型形式,无需提前知晓具体Worksheet类型,只需确认它包含date属性即可按此排序:

if let diaryArray = vm.readData(of: [T](), from: "\(path)_data") {
    let sortedArray = diaryArray.sorted(by: {$0.date > $1.date})
    vm.worksheets = sortedArray
}

我尝试了两种实现方式,但都存在问题:


1. 协议方式

先定义包含公共属性的协议,让所有Worksheet结构体遵循该协议:

protocol Worksheet: Codable, Identifiable, Hashable {
    var id: String { get set }
    var date: Date { get set }
}

extension Worksheet {
    static func parseVehicleFields(id: String, date: Date) -> (String, Date) {
        let id = ""
        let date = Date()
        return (id, date)
    }
}

struct Diary: Worksheet {
    var id: String
    var date: Date
    
    var title: String
    var situation: String
    var thoughts: String
    var emotions: String
    var physicalSensations: String
    var behaviours: String
    var analysis: String
    var alternative: String
    var outcome: String

    init() {
        self.id = ""
        self.date = Date()
        self.title = ""
        self.situation = ""
        self.thoughts = ""
        self.emotions = ""
        self.physicalSensations = ""
        self.behaviours = ""
        self.analysis = ""
        self.alternative = ""
        self.outcome = ""
    }
}

struct Goal: Worksheet {
    var id: String
    var date: Date
    
    var mainGoal: String
    var notificationActive: Bool
    var notificationDate: Date
    var obstacles: String
    var positiveBehaviours: String
    var immediateActions: String
    var goalAchieved: Bool

    init() {
        self.id = ""
        self.date = Date()
        self.mainGoal = ""
        self.notificationActive = false
        self.notificationDate = Date()
        self.obstacles = ""
        self.positiveBehaviours = ""
        self.immediateActions = ""
        self.goalAchieved = false
    }
}

遇到的问题

尝试使用[any Worksheet]作为类型读取数据时,编译报错:'Type 'any Worksheet' cannot conform to 'Decodable''

if let array = vm.readData(of: [any Worksheet](), from: "\(path)_data") {
    let sortedArray = array.sorted(by: {$0.date > $1.date})
    vm.worksheets = sortedArray
}

配套的保存函数是泛型实现,传入具体类型(如Diary)时能正常运行,但这种写法失去了泛型复用的意义:

func saveWorksheetProtocol<T: Worksheet>(for type: [T], to path: String) {
    let directoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
    let documentURL = (directoryURL?.appendingPathComponent(path).appendingPathExtension("json"))!
    let jsonEncoder = JSONEncoder()
    let data = try? jsonEncoder.encode(type)
    do {
        try data?.write(to: documentURL, options: .noFileProtection)
    } catch {
        print("Error...Cannot save data!!!See error: \(error.localizedDescription)")
    }
}

2. 嵌套结构体方式

将所有Worksheet类型嵌套到一个主结构体中:

struct Worksheet: Codable, Identifiable, Hashable {
    var id: String
    var date: Date
    var diary: Diary?
    var goal: Goal?
    var experiment: Experiment2?

    init() {
        self.id = ""
        self.date = Date()
        self.diary = Diary()
        self.goal = Goal()
        self.experiment = Experiment2()
    }
}

struct Diary: Codable, Hashable {
    var title: String
    var situation: String
    var thoughts: String
    var emotions: String
    var physicalSensations: String
    var behaviours: String
    var analysis: String
    var alternative: String
    var outcome: String

    init() {
        self.title = ""
        self.situation = ""
        self.thoughts = ""
        self.emotions = ""
        self.physicalSensations = ""
        self.behaviours = ""
        self.analysis = ""
        self.alternative = ""
        self.outcome = ""
    }
}

struct Goal: Codable, Hashable {
    var mainGoal: String
    var notificationActive: Bool
    var notificationDate: Date
    var obstacles: String
    var positiveBehaviours: String
    var immediateActions: String
    var goalAchieved: Bool

    init() {
        self.mainGoal = ""
        self.notificationActive = false
        self.notificationDate = Date()
        self.obstacles = ""
        self.positiveBehaviours = ""
        self.immediateActions = ""
        self.goalAchieved = false
    }
}

遇到的问题

  • 若子结构体为非可选类型,会产生冗余数据:比如仅需Diary条目时,仍需持有空的Goal、Experiment2实例
  • 若将子类型设为可选类型,使用Binding时会出现无法解析的解包错误,即使提前判断过实例是否存在:
TextField("", text: $entry.diary!.title, axis: .vertical) // 此处报错
    .lineLimit(20)
    .disabled(!editMode)

现寻求高效处理Worksheet类型的方案,以减少视图与函数的重复代码。

内容的提问来源于stack exchange,提问作者Peter Ruppert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 04:25:21