initial version
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
import Foundation
|
||||
import CoreData
|
||||
import Combine
|
||||
|
||||
class InvestmentSourceRepository: ObservableObject {
|
||||
private let context: NSManagedObjectContext
|
||||
|
||||
@Published private(set) var sources: [InvestmentSource] = []
|
||||
|
||||
init(context: NSManagedObjectContext = CoreDataStack.shared.viewContext) {
|
||||
self.context = context
|
||||
fetchSources()
|
||||
setupNotificationObserver()
|
||||
}
|
||||
|
||||
private func setupNotificationObserver() {
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(contextDidChange),
|
||||
name: .NSManagedObjectContextObjectsDidChange,
|
||||
object: context
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func contextDidChange(_ notification: Notification) {
|
||||
guard isRelevantChange(notification) else { return }
|
||||
fetchSources()
|
||||
}
|
||||
|
||||
// MARK: - Fetch
|
||||
|
||||
func fetchSources(account: Account? = nil) {
|
||||
context.perform { [weak self] in
|
||||
guard let self else { return }
|
||||
let request: NSFetchRequest<InvestmentSource> = InvestmentSource.fetchRequest()
|
||||
if let account = account {
|
||||
request.predicate = NSPredicate(format: "account == %@", account)
|
||||
}
|
||||
request.sortDescriptors = [
|
||||
NSSortDescriptor(keyPath: \InvestmentSource.name, ascending: true)
|
||||
]
|
||||
|
||||
do {
|
||||
self.sources = try self.context.fetch(request)
|
||||
} catch {
|
||||
print("Failed to fetch sources: \(error)")
|
||||
self.sources = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchSource(by id: UUID) -> InvestmentSource? {
|
||||
let request: NSFetchRequest<InvestmentSource> = InvestmentSource.fetchRequest()
|
||||
request.predicate = NSPredicate(format: "id == %@", id as CVarArg)
|
||||
request.fetchLimit = 1
|
||||
return try? context.fetch(request).first
|
||||
}
|
||||
|
||||
func fetchSources(for category: Category) -> [InvestmentSource] {
|
||||
let request: NSFetchRequest<InvestmentSource> = InvestmentSource.fetchRequest()
|
||||
request.predicate = NSPredicate(format: "category == %@", category)
|
||||
request.sortDescriptors = [
|
||||
NSSortDescriptor(keyPath: \InvestmentSource.name, ascending: true)
|
||||
]
|
||||
|
||||
return (try? context.fetch(request)) ?? []
|
||||
}
|
||||
|
||||
func fetchActiveSources() -> [InvestmentSource] {
|
||||
sources.filter { $0.isActive }
|
||||
}
|
||||
|
||||
func fetchSourcesNeedingUpdate(for account: Account? = nil) -> [InvestmentSource] {
|
||||
let filtered = account == nil ? sources : sources.filter { $0.account?.id == account?.id }
|
||||
return filtered.filter { $0.needsUpdate }
|
||||
}
|
||||
|
||||
// MARK: - Create
|
||||
|
||||
@discardableResult
|
||||
func createSource(
|
||||
name: String,
|
||||
category: Category,
|
||||
notificationFrequency: NotificationFrequency = .monthly,
|
||||
customFrequencyMonths: Int = 1,
|
||||
account: Account? = nil
|
||||
) -> InvestmentSource {
|
||||
let source = InvestmentSource(context: context)
|
||||
source.name = name
|
||||
source.category = category
|
||||
source.notificationFrequency = notificationFrequency.rawValue
|
||||
source.customFrequencyMonths = Int16(customFrequencyMonths)
|
||||
source.account = account
|
||||
|
||||
save()
|
||||
return source
|
||||
}
|
||||
|
||||
// MARK: - Update
|
||||
|
||||
func updateSource(
|
||||
_ source: InvestmentSource,
|
||||
name: String? = nil,
|
||||
category: Category? = nil,
|
||||
notificationFrequency: NotificationFrequency? = nil,
|
||||
customFrequencyMonths: Int? = nil,
|
||||
isActive: Bool? = nil,
|
||||
account: Account? = nil
|
||||
) {
|
||||
if let name = name {
|
||||
source.name = name
|
||||
}
|
||||
if let category = category {
|
||||
source.category = category
|
||||
}
|
||||
if let frequency = notificationFrequency {
|
||||
source.notificationFrequency = frequency.rawValue
|
||||
}
|
||||
if let customMonths = customFrequencyMonths {
|
||||
source.customFrequencyMonths = Int16(customMonths)
|
||||
}
|
||||
if let isActive = isActive {
|
||||
source.isActive = isActive
|
||||
}
|
||||
if let account = account {
|
||||
source.account = account
|
||||
}
|
||||
|
||||
save()
|
||||
}
|
||||
|
||||
func toggleActive(_ source: InvestmentSource) {
|
||||
source.isActive.toggle()
|
||||
save()
|
||||
}
|
||||
|
||||
// MARK: - Delete
|
||||
|
||||
func deleteSource(_ source: InvestmentSource) {
|
||||
context.delete(source)
|
||||
save()
|
||||
}
|
||||
|
||||
func deleteSource(at offsets: IndexSet) {
|
||||
for index in offsets {
|
||||
guard index < sources.count else { continue }
|
||||
context.delete(sources[index])
|
||||
}
|
||||
save()
|
||||
}
|
||||
|
||||
// MARK: - Statistics
|
||||
|
||||
var sourceCount: Int {
|
||||
sources.count
|
||||
}
|
||||
|
||||
var activeSourceCount: Int {
|
||||
sources.filter { $0.isActive }.count
|
||||
}
|
||||
|
||||
var totalValue: Decimal {
|
||||
sources.reduce(Decimal.zero) { $0 + $1.latestValue }
|
||||
}
|
||||
|
||||
func topSources(limit: Int = 5) -> [InvestmentSource] {
|
||||
sources
|
||||
.sorted { $0.latestValue > $1.latestValue }
|
||||
.prefix(limit)
|
||||
.map { $0 }
|
||||
}
|
||||
|
||||
// MARK: - Freemium Check
|
||||
|
||||
var canAddMoreSources: Bool {
|
||||
// This will be checked against FreemiumValidator
|
||||
true
|
||||
}
|
||||
|
||||
// MARK: - Save
|
||||
|
||||
private func save() {
|
||||
guard context.hasChanges else { return }
|
||||
do {
|
||||
try context.save()
|
||||
fetchSources()
|
||||
CoreDataStack.shared.refreshWidgetData()
|
||||
} catch {
|
||||
print("Failed to save context: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func isRelevantChange(_ notification: Notification) -> Bool {
|
||||
guard let info = notification.userInfo else { return false }
|
||||
let keys: [String] = [
|
||||
NSInsertedObjectsKey,
|
||||
NSUpdatedObjectsKey,
|
||||
NSDeletedObjectsKey,
|
||||
NSRefreshedObjectsKey
|
||||
]
|
||||
|
||||
for key in keys {
|
||||
if let objects = info[key] as? Set<NSManagedObject> {
|
||||
if objects.contains(where: { $0 is InvestmentSource }) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user