I have a Swift program that works as desired. I have 3 view models that each call a separate model. Each model calls a function that reads a separate large CSV file, performs some manipulation and returns a data frame. This takes some time and I would like to speed things up.
Swift offers a DispatchQueue
that allows one to place code into an asynchronous global queue with QOS and I believe if I ran the creation of the view models in this fashion, I would display the initial view sooner.
The problem is: I have no idea how to incorporate it. Any help to point me in the right direction will be appreciated.
Below is my content view, one view model, and one model. The test dispatch queue code at the end runs successfully in a playground.
struct ContentView: View {
@StateObject var vooVM: VOOViewModel = VOOViewModel()
@StateObject var vfiaxVM: VFIAXViewModel = VFIAXViewModel()
@StateObject var principalVM: PrincipalViewModel = PrincipalViewModel()
@State private var selectedItemId: Int?
var body: some View {
NavigationView {
VStack (alignment: .leading) {
List {
Spacer()
.frame(height: 20)
Group {
Divider()
NavigationLink(destination: Summary(vooVM: vooVM, vfiaxVM: vfiaxVM, prinVM: principalVM), tag: 1, selection: $selectedItemId, label: {
HStack {
Image(systemName: "house")
.padding(.leading, 10)
.padding(.trailing, 0)
.padding(.bottom, 5)
Text("Summary")
.bold()
.padding(.bottom, 2)
} // end h stack
})
} // end group
NavigationLinks(listText: "VOO", dataFrame1: vooVM.timeSeriesDailyDF1, dataFrame5: vooVM.timeSeriesDailyDF5)
NavigationLinks(listText: "VFIAX", dataFrame1: vfiaxVM.timeSeriesDailyDF1, dataFrame5: vfiaxVM.timeSeriesDailyDF5)
NavigationLinks(listText: "Principal", dataFrame1: principalVM.timeSeriesDailyDF1, dataFrame5: principalVM.timeSeriesDailyDF5)
Divider()
Spacer()
} // end list
} // end v stack
} // end navigation view
.onAppear {self.selectedItemId = 1}
.navigationTitle("Stock Data")
.frame(width: 1200, height: 900, alignment: .center)
} // end body view
} // end content view
View Model
class VOOViewModel: ObservableObject {
@Published private var vooModel: VOOModel = VOOModel()
var timeSeriesDailyDF1: DataFrame {
return vooModel.vooDF.0
}
var timeSeriesDailyDF5: DataFrame {
return vooModel.vooDF.1
}
var symbol: String {
return vooModel.symbol
}
var currentShares: Double {
return vooModel.currentShares
}
var currentSharePrice: Double {
let lastRowIndex: Int = vooModel.vooDF.0.shape.rows - 1
let currentPrice: Double = (vooModel.vooDF.0[row: lastRowIndex])[1] as! Double
return currentPrice
}
var percentGain: Double {
let pastValue: Double = (vooModel.vooDF.0[row: 0])[1] as! Double
let numRows: Int = vooModel.vooDF.0.shape.rows - 1
let curValue: Double = (vooModel.vooDF.0[row: numRows])[1] as! Double
let oneYearGain: Double = (100 * (curValue - pastValue)) / pastValue
return oneYearGain
}
}
Model
struct VOOModel {
var vooDF = GetDF(fileName: "FormattedVOO")
let symbol: String = "VOO"
let currentShares: Double = 1
}
Playground Code
let myQue = DispatchQueue.global()
let myGroup = DispatchGroup()
myQue.async(group: myGroup) {
sleep(5)
print("Task 1 complete")
}
myQue.async(group: myGroup) {
sleep(3)
print("Task 2 complete")
}
myGroup.wait()
print("All tasks completed")
I was able to solve my problem by using only 1 viewmodel instead of 3. The viewmodel calls all three models which were modified such that their function call to read a CSV file and place it into a dataframe is contained in a function. This function is in turn called within a function in the viewmodel which is called in the viewmodels init. Below is the updated code. Note that the ContentView was simplified to make testing easy.
New Content View:
struct ContentView: View {
@StateObject var viewModel: ViewModel = ViewModel()
var body: some View {
let printValue1 = (viewModel.dataFrames.0.0[row: 0])[0]
let tempValue = (viewModel.dataFrames.0.0[row: 0])[1] as! Double
let tempValueFormatted: String = String(format: "$%.2f", tempValue)
Text("\(dateToStringFormatter.string(from: printValue1 as! Date))" + " " + tempValueFormatted )
.frame(width: 1200, height: 900, alignment: .center)
}
}
New ViewModel:
class ViewModel: ObservableObject {
@Published private var vooModel: VOOModel = VOOModel()
@Published private var vfiaxModel: VFIAXModel = VFIAXModel()
@Published private var principalModel: PrincipalModel = PrincipalModel()
var dataFrames = ((DataFrame(), DataFrame()), (DataFrame(), DataFrame()), (DataFrame(), DataFrame()))
init() {
self.dataFrames = GetDataFrames()
}
func GetDataFrames() -> ((DataFrame, DataFrame), (DataFrame, DataFrame), (DataFrame, DataFrame)) {
let myQue: DispatchQueue = DispatchQueue.global()
let myGroup: DispatchGroup = DispatchGroup()
var vooDF = (DataFrame(), DataFrame())
var vfiaxDF = (DataFrame(), DataFrame())
var principalDF = (DataFrame(), DataFrame())
myQue.async(group: myGroup) {
vfiaxDF = self.vfiaxModel.GetData()
}
myQue.async(group: myGroup) {
principalDF = self.principalModel.GetData()
}
myQue.async(group: myGroup) {
vooDF = self.vooModel.GetData()
}
myGroup.wait()
return (vooDF, vfiaxDF, principalDF)
}
}
One of the new models. The other 2 are identical except for the CSV file they read.
struct VOOModel {
let symbol: String = "VOO"
let currentShares: Double = 1
func GetData() -> (DataFrame, DataFrame) {
let vooDF = GetDF(fileName: "FormattedVOO")
return vooDF
}
}