Skip to content

Commit

Permalink
Merge pull request #50 from moneymanagerex/ipad
Browse files Browse the repository at this point in the history
make DataManager as EnvironmentObject
  • Loading branch information
guanlisheng authored Sep 26, 2024
2 parents 9b9b1d6 + 985e77b commit 56c3e2e
Show file tree
Hide file tree
Showing 22 changed files with 152 additions and 170 deletions.
31 changes: 20 additions & 11 deletions MMEX/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ struct ContentView: View {
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@Environment(\.verticalSizeClass) var verticalSizeClass

@EnvironmentObject var dataManager: DataManager // Access DataManager from environment

var body: some View {
ZStack {
if let url = selectedFileURL {
Expand All @@ -29,11 +31,13 @@ struct ContentView: View {
TabContentView(selectedTab: $selectedTab, isDocumentPickerPresented: $isDocumentPickerPresented, databaseURL: url)
}
} else {
// Use @StateObject to manage the lifecycle of InfotableViewModel
let infotableViewModel = InfotableViewModel(dataManager: dataManager)
// iPhone layout: Tabs at the bottom
TabView(selection: $selectedTab) {
// Latest Transactions Tab
NavigationView {
TransactionListView2(databaseURL: url) // Summary and Edit feature
TransactionListView2(viewModel: infotableViewModel) // Summary and Edit feature
.navigationBarTitle("Latest Transactions", displayMode: .inline)
}
.tabItem {
Expand All @@ -44,7 +48,7 @@ struct ContentView: View {

// Insights module
NavigationView {
InsightsView(viewModel: InsightsViewModel(databaseURL: url))
InsightsView(viewModel: InsightsViewModel(dataManager: dataManager))
.navigationBarTitle("Reports and Insights", displayMode: .inline)
}
.tabItem {
Expand All @@ -55,7 +59,7 @@ struct ContentView: View {

// Add Transactions Tab
NavigationView {
TransactionAddView2(databaseURL: url, selectedTab: $selectedTab) // Reuse or new transaction add
TransactionAddView2(selectedTab: $selectedTab) // Reuse or new transaction add
.navigationBarTitle("Add Transaction", displayMode: .inline)
}
.tabItem {
Expand All @@ -66,7 +70,7 @@ struct ContentView: View {

// Combined Management Tab
NavigationView {
ManagementView(databaseURL: url, isDocumentPickerPresented: $isDocumentPickerPresented)
ManagementView(isDocumentPickerPresented: $isDocumentPickerPresented)
.navigationBarTitle("Management", displayMode: .inline)
}
.tabItem {
Expand All @@ -77,7 +81,7 @@ struct ContentView: View {

// Settings Tab
NavigationView {
SettingsView(viewModel: InfotableViewModel(databaseURL: url)) // Payees, Accounts, Currency
SettingsView(viewModel: infotableViewModel) // Payees, Accounts, Currency
.navigationBarTitle("Settings", displayMode: .inline)
}
.tabItem {
Expand Down Expand Up @@ -118,6 +122,7 @@ struct ContentView: View {
if let selectedURL = urls.first {
if selectedURL.startAccessingSecurityScopedResource() {
selectedFileURL = selectedURL
dataManager.openDabase(at: selectedURL)
UserDefaults.standard.set(selectedURL.path, forKey: "SelectedFilePath")
selectedURL.stopAccessingSecurityScopedResource()
} else {
Expand Down Expand Up @@ -147,7 +152,7 @@ struct ContentView: View {
} else {
print("Unable to access file at URL: \(url)")
}
let dataManager = DataManager(databaseURL: url)
dataManager.openDabase(at: url)
let repository = dataManager.getRepository()
guard let tables = Bundle.main.url(forResource: "tables.sql", withExtension: "") else {
print("Cannot find tables.sql in bundle")
Expand Down Expand Up @@ -193,26 +198,30 @@ struct SidebarView: View {
struct TabContentView: View {
@Binding var selectedTab: Int
@Binding var isDocumentPickerPresented: Bool
@EnvironmentObject var dataManager: DataManager // Access DataManager from environment

var databaseURL: URL

var body: some View {
// Use @StateObject to manage the lifecycle of InfotableViewModel
let infotableViewModel = InfotableViewModel(dataManager: dataManager)
// Here we ensure that there's no additional NavigationStack or NavigationView
Group {
switch selectedTab {
case 0:
TransactionListView2(databaseURL: databaseURL)
TransactionListView2(viewModel: infotableViewModel) // Summary and Edit feature
.navigationBarTitle("Latest Transactions", displayMode: .inline)
case 1:
InsightsView(viewModel: InsightsViewModel(databaseURL: databaseURL))
InsightsView(viewModel: InsightsViewModel(dataManager: dataManager))
.navigationBarTitle("Reports and Insights", displayMode: .inline)
case 2:
TransactionAddView2(databaseURL: databaseURL, selectedTab: $selectedTab)
TransactionAddView2(selectedTab: $selectedTab)
.navigationBarTitle("Add Transaction", displayMode: .inline)
case 3:
ManagementView(databaseURL: databaseURL, isDocumentPickerPresented: $isDocumentPickerPresented)
ManagementView(isDocumentPickerPresented: $isDocumentPickerPresented)
.navigationBarTitle("Management", displayMode: .inline)
case 4:
SettingsView(viewModel: InfotableViewModel(databaseURL: databaseURL))
SettingsView(viewModel: infotableViewModel)
.navigationBarTitle("Settings", displayMode: .inline)
default:
EmptyView()
Expand Down
30 changes: 27 additions & 3 deletions MMEX/DatabaseManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@
import Foundation
import SQLite

class DataManager {
let db: Connection?

class DataManager: ObservableObject {
@Published var isDatabaseConnected = false
private(set) var db: Connection?
private(set) var databaseURL: URL?

init() {
// TODO
}

init(databaseURL: URL) {
if databaseURL.startAccessingSecurityScopedResource() {
defer { databaseURL.stopAccessingSecurityScopedResource() }
Expand All @@ -29,6 +35,24 @@ class DataManager {
}
}

func openDabase(at databaseURL: URL) {
self.databaseURL = databaseURL
if databaseURL.startAccessingSecurityScopedResource() {
defer { databaseURL.stopAccessingSecurityScopedResource() }
do {
db = try Connection(databaseURL.path)
print("Connected to database: \(databaseURL.path)")
setJournalModeDelete(connection: db!)
} catch {
print("Failed to connect to database: \(error)")
db = nil
}
} else {
db = nil
print("Failed to access security-scoped resource")
}
}

func setJournalModeDelete(connection: Connection) {
do {
try connection.execute("PRAGMA journal_mode = MEMORY;")
Expand Down
3 changes: 3 additions & 0 deletions MMEX/MMEXApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import SwiftUI

@main
struct MMEXApp: App {
@StateObject private var dataManager = DataManager()

var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(dataManager)
}
}
}
11 changes: 6 additions & 5 deletions MMEX/ViewModels/InfotableViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import SQLite
import Combine

class InfotableViewModel: ObservableObject {
let databaseURL: URL

// for the view to observe
@Published var baseCurrencyId: Int64 = 0
@Published var defaultAccountId: Int64 = 0
Expand All @@ -33,9 +31,8 @@ class InfotableViewModel: ObservableObject {
@Published var txns: [TransactionData] = []
@Published var txns_per_day: [String: [TransactionData]] = [:]

init(databaseURL: URL) {
self.databaseURL = databaseURL
self.dataManager = DataManager(databaseURL: databaseURL)
init(dataManager: DataManager) {
self.dataManager = dataManager

self.infotableRepo = self.dataManager.getInfotableRepository()
self.transactionRepo = self.dataManager.getTransactionRepository()
Expand Down Expand Up @@ -69,6 +66,10 @@ class InfotableViewModel: ObservableObject {
func getSchemaVersion() -> Int32 {
return self.dataManager.getSchemaVersion() ?? 0
}

func getDatabaseURL() -> URL {
return self.dataManager.databaseURL!
}

// Set up individual bindings for each @Published property
private func setupBindings() {
Expand Down
8 changes: 4 additions & 4 deletions MMEX/ViewModels/InsightsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SwiftUI
import Combine

class InsightsViewModel: ObservableObject {
let databaseURL: URL
private var dataManager: DataManager

// Published properties for the view to observe
@Published var stats: [TransactionData] = []
Expand All @@ -19,8 +19,8 @@ class InsightsViewModel: ObservableObject {

private var cancellables = Set<AnyCancellable>()

init(databaseURL: URL) {
self.databaseURL = databaseURL
init(dataManager: DataManager) {
self.dataManager = dataManager
self.startDate = Calendar.current.date(byAdding: .month, value: -1, to: Date()) ?? Date()
self.endDate = Date()

Expand All @@ -37,7 +37,7 @@ class InsightsViewModel: ObservableObject {
}

func loadTransactions() {
let repository = DataManager(databaseURL: self.databaseURL).getTransactionRepository()
let repository = dataManager.getTransactionRepository()

// Fetch transactions asynchronously
DispatchQueue.global(qos: .background).async {
Expand Down
8 changes: 3 additions & 5 deletions MMEX/Views/Accounts/AccountDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import SwiftUI

struct AccountDetailView: View {
@State var account: AccountWithCurrency
let databaseURL: URL
@EnvironmentObject var dataManager: DataManager // Access DataManager from environment
@Binding var currencies: [CurrencyData] // Bind to the list of available currencies

@State private var editingAccount = AccountWithCurrency()
Expand Down Expand Up @@ -107,7 +107,7 @@ struct AccountDetailView: View {
}

func saveChanges() {
let repository = DataManager(databaseURL: databaseURL).getAccountRepository()
let repository = dataManager.getAccountRepository()
if repository.update(account.data) {
// Successfully updated
} else {
Expand All @@ -116,7 +116,7 @@ struct AccountDetailView: View {
}

func deleteAccount() {
let repository = DataManager(databaseURL: databaseURL).getAccountRepository()
let repository = dataManager.getAccountRepository()
if repository.delete(account.data) {
presentationMode.wrappedValue.dismiss()
} else {
Expand All @@ -128,14 +128,12 @@ struct AccountDetailView: View {
#Preview {
AccountDetailView(
account: AccountData.sampleDataWithCurrency[0],
databaseURL: URL(string: "path/to/database")!,
currencies: .constant(CurrencyData.sampleData))
}

#Preview {
AccountDetailView(
account: AccountData.sampleDataWithCurrency[1],
databaseURL: URL(string: "path/to/database")!,
currencies: .constant(CurrencyData.sampleData)
)
}
17 changes: 6 additions & 11 deletions MMEX/Views/Accounts/AccountListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,13 @@
import SwiftUI

struct AccountListView: View {
let databaseURL: URL
@EnvironmentObject var dataManager: DataManager // Access DataManager from environment
@State private var currencies: [CurrencyData] = []
@State private var accounts_by_type: [String:[AccountWithCurrency]] = [:]
@State private var newAccount = AccountWithCurrency()
@State private var isPresentingAccountAddView = false
@State private var expandedSections: [String: Bool] = [:] // Tracks the expanded/collapsed state

private var repository: AccountRepository

init(databaseURL: URL) {
self.databaseURL = databaseURL
self.repository = DataManager(databaseURL: databaseURL).getAccountRepository()
}

var body: some View {
NavigationStack {
List {
Expand Down Expand Up @@ -52,7 +45,7 @@ struct AccountListView: View {
// Show account list based on expandedSections state
if expandedSections[accountType] == true {
ForEach(accounts_by_type[accountType]!) { account in
NavigationLink(destination: AccountDetailView(account: account, databaseURL: databaseURL, currencies: $currencies)) {
NavigationLink(destination: AccountDetailView(account: account, currencies: $currencies)) {
HStack {
Text(account.data.name)
.font(.subheadline)
Expand Down Expand Up @@ -103,6 +96,7 @@ struct AccountListView: View {
func loadAccounts() {
print("Loading payees in AccountListView...")

let repository = dataManager.getAccountRepository()
DispatchQueue.global(qos: .background).async {
let loadedAccounts = repository.loadWithCurrency()
DispatchQueue.main.async {
Expand All @@ -115,7 +109,7 @@ struct AccountListView: View {
}

func loadCurrencies() {
let repo = DataManager(databaseURL: self.databaseURL).getCurrencyRepository()
let repo = dataManager.getCurrencyRepository()

DispatchQueue.global(qos: .background).async {
let loadedCurrencies = repo.load()
Expand All @@ -127,6 +121,7 @@ struct AccountListView: View {
}

func addAccount(account: inout AccountWithCurrency) {
let repository = dataManager.getAccountRepository()
if repository.insert(&(account.data)) {
// self.accounts.append(account)
self.loadAccounts()
Expand All @@ -135,5 +130,5 @@ struct AccountListView: View {
}

#Preview {
AccountListView(databaseURL: URL(string: "path/to/database")!)
AccountListView()
}
8 changes: 4 additions & 4 deletions MMEX/Views/Assets/AssetDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import SwiftUI

struct AssetDetailView: View {
@EnvironmentObject var dataManager: DataManager // Access DataManager from environment
@Binding var asset: AssetData
let databaseURL: URL

@State private var editingAsset = AssetData()
@State private var isPresentingEditView = false
Expand Down Expand Up @@ -118,7 +118,7 @@ struct AssetDetailView: View {
}

func saveChanges() {
let repository = DataManager(databaseURL: databaseURL).getAssetRepository() // pass URL here
let repository = dataManager.getAssetRepository() // pass URL here
if repository.update(asset) {
// TODO
} else {
Expand All @@ -127,7 +127,7 @@ struct AssetDetailView: View {
}

func deleteAsset(){
let repository = DataManager(databaseURL: databaseURL).getAssetRepository() // pass URL here
let repository = dataManager.getAssetRepository() // pass URL here
if repository.delete(asset) {
// Dismiss the AssetDetailView and go back to the previous view
presentationMode.wrappedValue.dismiss()
Expand All @@ -149,5 +149,5 @@ struct AssetDetailView: View {
}

#Preview {
AssetDetailView(asset: .constant(AssetData.sampleData[0]), databaseURL: URL(string: "path/to/database")!)
AssetDetailView(asset: .constant(AssetData.sampleData[0]))
}
Loading

0 comments on commit 56c3e2e

Please sign in to comment.