@Attribute 宏定义属性

温馨提示:本文最后更新于2025-01-18 18:47:27,某些文章具有时效性,若有错误或已失效,请在下方留言

框架默认的行为适用于大多数使用场景,能够有效管理模型类的存储属性。然而,如果您需要更改特定属性的持久化行为,可以使用 @Attribute 宏对其进行注解。例如,您可以通过指定某个属性的值在该模型的所有实例中唯一,从而避免模型数据的冲突。

@Model class RemoteImage {
    @Attribute(.unique) var sourceURL: URL
    var data: Data
    init(sourceURL: URL, data: Data = Data()) {
        self.sourceURL = sourceURL
        self.data = data
    }
}

存储在外部文件中

SwiftData 适合存储字符串整数日期Codable对象,但对于较大的数据(如图像视频),最好将其存储在外部文件中,并在 SwiftData 对象中引用文件名。

使用.externalStorage 属性,SwiftData 可以将数据存储在主存储之外,仅保存外部文件名,并自动连接二者,外部数据的行为与内部数据类似。

例如,存储游戏玩家头像的代码如下:

@Model class User {
    var name: String
    var score: Int
    @Attribute(.externalStorage) var avatar: Data

    init(name: String, score: Int, avatar: Data) {
        self.name = name
        self.score = score
        self.avatar = avatar
    }
}

可以将其与其他属性组合,如加密:

@Attribute(.externalStorage, .allowsCloudEncryption) var avatar: Data

1. externalStorage 属性只是建议,SwiftData 可能仍会在主存储中保存小于约 128K 的 Data 对象。
2. 使用 String 类型时,SwiftData 可无限制存储字符串,不必使用外部文件。
3. 数据的存储位置对我们来说并不重要,但不能在谓词中使用外部存储的属性,因为外部文件对底层数据不可见。遇到相关错误时,请注意可能的陷阱。

@Attribute 参数

  • options:应用于附加属性的一系列选项,用于自定义其行为。有关可能的值,请参见 Schema.Attribute.Option
  • originalName:属性的先前名称,如果与当前模式版本中的名称不同。默认值为 nil
  • hashModifier :表示附加属性最新版本的唯一哈希值。默认值为 nil
属性选项描述
allowsCloudEncryption将属性的值以加密形式存储。
externalStorage将属性的值作为二进制数据存储在模型存储旁边。
preserveValueOnDeletion当上下文删除拥有模型时,保留属性的值在持久化历史中。
spotlight将属性的值进行索引,以便出现在 Spotlight 搜索结果中。
unique确保属性的值在相同类型的所有模型中是唯一的。
transformable(by: ValueTransformer.Type)在内存形式和持久化形式之间转换属性的值。
transformable(by: String) 
Schema.Attribute.Option

创建派生属性

在 Swift 中,派生属性可以自动更新以反映模型中的更改。以下代码展示了如何创建一个 Player 类,其中包含一个lastModified属性,以及一个调整任意值的 update()方法,这个方法同时会将 lastModified 更新为当前日期和时间。

@Model class Player {
    var name: String
    var score: Int
    var lastModified: Date
    init(name: String, score: Int) {
        self.name = name
        self.score = score
        self.lastModified = .now
    }

    func update<T>(keyPath: ReferenceWritableKeyPath<Player, T>, to value: T) {
        self[keyPath: keyPath] = value
        lastModified = .now
    }
}

有了这个实现,我们可以使用 item.update(keyPath:\.score, to:10)来更改 score 属性, 同时更新 lastModified

SwiftData 属性不支持 willSetdidset 属性观察者,因此我们实际上是通过一个新方法来传递我们的更改,以代替使用 didSet

下面示例演示如何在 SwiftUl 中使用这个 Player 类。在这个示例中,当更新 score 属性时, lastModified 字段也会被相应地更新:

struct ContentView: View {
    @Environment(\.modelContext) private var context
    @Query private var players: [Player]

    var body: some View {
        NavigationSplitView {
            List {
                ForEach(players) { item in
                    NavigationLink {
                        Text("Item at \(item.lastModified, format: Date.FormatStyle(date: .numeric, time: .standard))")
                        Text("\(item.score)")
                        Button(action: updateItem) {
                            Label("Update Item", systemImage: "pencil.line")
                        }
                    } label: {
                        Text(item.lastModified, format: Date.FormatStyle(date: .numeric, time: .standard))
                    }
                }
                .onDelete(perform: deleteItems)
            }
        } detail: {
            Text("Select an item")
        }
    }
    
    private func updateItem() {
        withAnimation {
            if let item = players.first {
                item.update(keyPath: \.score, to: 10)
            }
        }
    }
}

通过这种方式,我们不仅可以有效管理模型属性的更新,还能确保相关的lastModified 属性始终保持最新。

派生属性另外一个技巧

下面示例更新 students 数据,会自动更新 studentCount 这是一个变通的技巧

@Model class School {
    var students: [Student]
    var studentCount: Int {
        students.count
    }
    init(students: [Student]) {
        self.students = students
    }
}

@Model class Student {
    var name: String
    init(name: String) {
        self.name = name
    }
}

加密 SwiftData

如果您存储的信息特别敏感,可以采取一到两步措施,以确保数据的安全存储。最简单且确保有效的选项是对任何应在iCloud 中以加密形式存储的属性使用 .allowsCloudEncryption 属性,示例如下:

@Attribute(.allowsCloudEncryption) var cardNumber: Int

顾名思义,这种加密仅在iCloud 中有效;此属性对本地存储的数据没有影响。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容