Hi, first if you are making a framework / library (stateless in many cases) your FileItem is perfect.
Active Record Pattern
struct FileItem {
var name: String
var size: Int64
static var all: [FileItem] = { ... }
static var favourites: [FileItem] = { ... }
func setAsFavourite(isFavourite: Bool) { ... }
}
Repository Pattern
struct FileItem {
var name: String
var size: Int64
}
class FileItemRepository {
func getAll() -> [FileItem] { ... }
func getFavourites() -> [FileItem] { ... }
func setFile(_ file: FileItem, asFavourite: Bool) { ... }
}
If you are making an App (stateful) you need a state. Think about single source of truth(s) and use ObservableObject for external state, you can have one or many state objects. All depends on your app needs but Keep It Simple. You can keep your FileItem with tasks or not, depends.
Example #1 (assuming favorite is a file attribute or a reference)
struct FileItem {
var name: String
var size: Int64
func setAsFavourite(isFavourite: Bool) { ... }
}
class FileStore: ObservableObject {
@Published var all: [FileItem] = []
var favourites: [FileItem] { … } // filter favourites from all
var isLoading: Bool = false // if needed
var error: Error? = nil // if needed
func load() async { … } // load all files, manage states (loading, error) if needed
}
struct MyApp: App {
@StateObject var store = FileStore()
var body: some Scene {
WindowGroup {
NavigationView {
MyListView(.all)
MyListView(.favourites)
}
.environmentObject(store)
.task { await store.load() }
}
}
}
Example #2.1 (assuming favorite is another file)
struct FileItem {
var name: String
var size: Int64
}
class FileStore: ObservableObject {
@Published var all: [FileItem] = []
@Published var favourites: [FileItem] = []
var isLoading: Bool = false // if needed
var error: Error? = nil // if needed
func load() async { … } // load all files and favourites files, manage states (loading, error) if needed
func setFile(_ file: FileItem, asFavourite: Bool) { ... }
}
struct MyApp: App {
@StateObject var store = FileStore()
var body: some Scene {
WindowGroup {
NavigationView {
MyListView(.all)
MyListView(.favourites)
}
.environmentObject(store)
.task { await store.load() }
}
}
}
Example #2.2 (assuming favorite is another file)
struct FileItem {
var name: String
var size: Int64
}
class FileStore: ObservableObject {
@Published var files: [FileItem] = []
enum Source {
case all
case favourites
}
var source: Source
var isLoading: Bool = false // if needed
var error: Error? = nil // if needed
func load() async { … } // load all files or favourites files, manage states (loading, error) if needed
func setFile(_ file: FileItem, asFavourite: Bool) { ... }
}
struct MyApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
MyListView(FileStore(.all))
MyListView(FileStore(.favourites))
}
}
}
}
…or…
struct MyApp: App {
@StateObject var allFileStore = FileStore(.all)
@StateObject var favouriteFileStore = FileStore(.favourites)
var body: some Scene {
WindowGroup {
NavigationView {
MyListView()
.environmentObject(allFileStore)
MyListView()
.environmentObject(favouriteFileStore)
}
}
}
}
Example #2.3 (assuming favorite is another file)
struct FileItem {
var name: String
var size: Int64
}
class FileStore: ObservableObject {
@Published var files: [FileItem] = []
var isLoading: Bool = false // if needed
var error: Error? = nil // if needed
open func load() async { … } // to subclass
func setFile(_ file: FileItem, asFavourite: Bool) { ... }
}
class AllFileStore: FileStore {
open func load() async { … } // load all files, manage states (loading, error) if needed
}
class FavouritesFileStore: FileStore {
open func load() async { … } // load favourites files, manage states (loading, error) if needed
}
struct MyApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
MyListView(AllFileStore())
MyListView(FavouriteFileStore())
}
}
}
}
…or…
struct MyApp: App {
@StateObject var allFileStore = AllFileStore()
@StateObject var favouriteFileStore = FavouriteFileStore()
var body: some Scene {
WindowGroup {
NavigationView {
MyListView()
.environmentObject(allFileStore)
MyListView()
.environmentObject(favouriteFileStore)
}
}
}
}
Tips:
Don’t think ViewModel, think state (view independent) that is part of your model
You can have one or many ObservableObjects (external state)
Use ObservableObjects when needed and don’t forget view local state
EnvironmentObject is your best friend!
Keep It Simple
In my 3 professional / client SwiftUI apps I made, I learned that EnvironmentObject is critical for many situations. Also, many things become problematic (or impossible) if I think about ViewModels.
Topic:
UI Frameworks
SubTopic:
SwiftUI
Tags: