During the development of a swiftUI project, I wish to have option to switch the way data is set, either
- Allowing API calls to be made as normal which sets an @Published variable, or
- Set the @published variable from mocked file and NOT make the api call.
The reason is that I am limited to the number of api calls per minute.
In my example below I load the mocked data in a model called "Person".
Current solution
- Set a global variable to distinguish between the two above mentioned states.
- In all places where api calls were be made, I introduce a condition to optionally use mocked data and not make the api call. See
.task
in MyView
struct GlobalConstants {
static let use_mock_data = true
}
class ViewModel: ObservableObject {
@Published var data: [Person] = []
@MainActor
func fetchData() async {
// ... data is set in this code
}
}
Within Person model, I set a static variable that returns the decoded mock data from a json file. The decode
method below is an extension to Bundle (Thanks Paul Hudsen).
extension Person {
static var mockPersons: [Person] {
Bundle.main.decode([Person].self, from: "persons.json")
}
}
struct MyView: View {
@StateObject var vm = PersonViewModel()
var body: some View {
NavigationView {
List {
ForEach(vm.data) { d in
NavigationLink {
OtherView(prop: d.detail)
} label: {
Text(d.name)
}
}
}
.task { // -----condition--------------------- //
if GlobalConstants.use_mock_data {
vm.data = Person.mockPersons
} else {
await vm.fetchData()
}
}
}
}
}
Question
What other approaches can I consider for enabling the two states? Overriding the methods in some way?
I am still on the learning curve to swift and wondering if theres a better way to enable long term maintenance in a clean and predictable way.
CodePudding user response:
As fetching the data belongs to the view model my suggestion is to put the condition into fetchData
.
Marking a method as async
doesn't require necessarily that the executed code is asynchronous
@MainActor
class ViewModel: ObservableObject {
@Published var people: [Person] = []
func fetchData() async {
if GlobalConstants.use_mock_data {
people = Person.mockPersons
} else {
// people = await callTheAPI()
}
}
}
and replace the task in the view with
.task {
await vm.fetchData()
}
Note: people
is a more meaningful name than data