Section 4/181 menit
4. @Model Lanjutan: Attribute, Constraints, dan External Storage
4. @Model Lanjutan: Attribute, Constraints, dan External Storage
@Attribute Options
swift
import SwiftData
@Model
final class Document {
// Unique constraint — insert duplikat = upsert (update existing)
@Attribute(.unique)
var remoteID: String
// External storage — data disimpan di file terpisah, bukan di SQLite row
// Cocok untuk: gambar, video, file besar (>1MB)
@Attribute(.externalStorage)
var thumbnailData: Data?
// Spotlight indexing — nilai ini bisa dicari via Spotlight
@Attribute(.spotlight)
var title: String
// Ephemeral — tidak disimpan ke store (hanya in-memory)
@Attribute(.ephemeral)
var isExpanded: Bool = false
// Transformable — type custom dengan Codable
@Attribute(.transformable(by: ColorTransformer.self))
var accentColor: UIColor?
// Encrypt — enkripsi nilai di SQLite (membutuhkan iOS 18+)
// @Attribute(.encrypt)
// var sensitiveNote: String?
var body: String
var createdAt: Date
init(remoteID: String, title: String, body: String) {
self.remoteID = remoteID
self.title = title
self.body = body
self.createdAt = Date()
}
}
// Custom ValueTransformer untuk UIColor
final class ColorTransformer: ValueTransformer {
override class func transformedValueClass() -> AnyClass { NSData.self }
override class func allowsReverseTransformation() -> Bool { true }
override func transformedValue(_ value: Any?) -> Any? {
guard let color = value as? UIColor else { return nil }
return try? NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: true)
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? Data else { return nil }
return try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data)
}
}
Composite Unique Constraint
swift
// Unique constraint pada kombinasi beberapa field (bukan satu field)
@Model
final class CalendarEvent {
// Unik berdasarkan kombinasi userID + date + title
@Attribute(.unique)
var compositeKey: String // "\(userID)-\(date.timeIntervalSince1970)-\(title)"
var userID: String
var date: Date
var title: String
var notes: String?
init(userID: String, date: Date, title: String) {
self.userID = userID
self.date = date
self.title = title
self.compositeKey = "\(userID)-\(Int(date.timeIntervalSince1970))-\(title)"
}
}
// Catatan: SwiftData belum support multi-column unique constraint secara native
// Workaround: composite key string seperti di atas
@Attribute dengan originalName untuk Rename Migration
swift
// Jika kamu rename property TANPA schema versioning (tidak direkomendasikan untuk breaking change),
// gunakan originalName agar SwiftData tahu mapping lama → baru
@Model
final class User {
// Property lama bernama "name", sekarang diganti "fullName"
@Attribute(originalName: "name")
var fullName: String
// Property lama bernama "email_address", sekarang "email"
@Attribute(originalName: "email_address")
var email: String
init(fullName: String, email: String) {
self.fullName = fullName
self.email = email
}
}