Related to the last question I asked, I'm trying to do an inventory system. And I would like to be able to use multiple datastores for it -- in-core (very useful for just debugging), Core Data, and Firestore being the three I've got right now.
Again, this is mostly from my head, not my actual (not-so-working, and, uh, clumsy as well as ugly) code. But I start out with something like:
protocol InventoryItem {
associatedtype idType
var id: idType { get }
var description: String { get set }
}
protocol InventoryStorage: InventoryItem {
associatedtype storageType
associatedtype itemType: InventoryItem
var kind: storageType { get }
var location: String { get }
var items: [itemType] { get set }
var itemCount: Int { get set }
}
protocol BookStorage: InventoryStorage {
var shelfCount: Int { get }
}
protocol RefrigeratedStorage: InventoryStorage {
var isFreezer: Bool { get }
var cubicFeet: Int { get }
}
class StorageArray<Kind: InventoryStorage> {
var units = [Kind]()
init(...) { ... }
func addItem(_ item: InventoryItem)
....
}
class Bookcase: InventoryStorage {
...
}
class Refrigerator: InventoryStorage {
...
}
class Freezer: InventoryStorage {
....
}
class AisleShelf: InventoryStorage {
...
}
class Store {
var bookcases: StorageArray<Bookcase>()
var refrigerators: StorageArray<Refrigerator>()
var freezers: StorageArray<Freezer>()
var shelves: StorageArray<AisleShelf>()
...
}
extension CoreDataInventoryItem : InventoryItem {
var id: UUID {
if self.cdID == nil {
self.cdID = UUID()
}
return self.cdID!
}
}
// And similarly for other core data types
and more along those lines. I'm using a protocol because, as I said, there are hopefully multiple datastores to hold this information, and I'd prefer to be able to decide at run time rather than build time :). (Also, InventoryStorage inherits from InventoryItem simply because they also have an id and a name; my ui code can then use the same views for some basic displays.)
I can get those basics to work, with in-core and Core Data (haven't done Firestore yet, but only because it requires a lot more setup). But then I can't figure out how to have both an in-core version and a Core Data version, and pass those along via @EnvironmentObject.
I ended up making the Store class a generic, a la
class Store<BookcaseType: BookStorage, RefrigeratorType: RefrigeratedStorage, ...> {
var bookcases: [BookcaseType]()
...
}
thinking I would then have something like
.environmentObject(Store<Bookcase, ...>())
or
.environmentObject(Store<CDBookcase, ...>())
But that doesn't work, because @EnvironmentObject needs to know the actual type, and using generics means it's a different type. (I've got a really ugly version using nothing but inherited classes, but my prototype protocol version is much cleaner, easier to read, and maintainable. Except for the whole, doesn't work part, of course.)
Wow this is long and a bit rambly, I'm sorry.