I have an interesting data modelling issue. In essence, in my app there will be a list of profiles, and then "profiles categories". There will always be a default category, the "All" category, which the user cannot delete/modify, and which contains (you guessed it) all the profiles. The user can also create their own "custom" categories, to which they can add profiles.
Naturally, I gravitated towards using an enum to model ProfilesCategory, since this seemed like a good use case for an either/or type, where the type category is either "All", or a custom one:
enum ProfilesCategory {
case all
case custom(name: String, profileIDs: Set<Profile.ID>)
}
However, the user is also able to add/delete profiles from profile categories – but enums with associated values don't allow in-place mutation, so I'd end up copying the profileIDs Set every time. This would be fine if the category had a small amount of profiles, but this isn't scalable.
So, I was wondering if there was a good alternative to using an enum with associated values such as this which gives me the same modelling characteristics and in-place mutation.
I wouldn't treat the "All" category as a different thing than custom categories. Instead I would treat it as a normal category that just happens to not constrain what is inside
struct Profile: Identifiable {
var id: Int
static var allProfiles: [Profile] {
[Profile(id: 123), Profile(id: 23), Profile(id: 42), Profile(id: 999)]
}
}
struct Category: Equatable {
var name: String
var filter: Set<Profile.ID>?
/// categories that will be shown to the user
static var allCategories: [Category] {
[Category(name: "All profiles", filter: nil)] + Self.customCategories
}
/// categories that can be modified by the user
static var customCategories: [Category] = [
Category(name: "funny profiles", filter: [42, 999]),
Category(name: "my favourite", filter: [42]),
]
func isCustom() -> Bool {
Self.customCategories.contains(self)
}
func shouldShowDeleteIconInTheUI() -> Bool {
self.isCustom()
}
func profilesInCategory() -> [Profile] {
if let ids = self.filter {
return Profile.allProfiles.filter({ ids.contains($0.id) })
} else {
return Profile.allProfiles
}
}
mutating func addProfile(id: Int) {
self.filter = self.filter ?? []
self.filter?.insert(id)
}
}