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")
1

There are 1 answers

0
Chris On BEST ANSWER

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
    }
}