Migrate from Core Data to SwiftData
- Replace Core Data with SwiftData for iOS 18+ - Create MoodEntryModel as @Model class replacing MoodEntry entity - Create SharedModelContainer for App Group container sharing - Create DataController with CRUD extensions replacing PersistenceController - Update all views and view models to use MoodEntryModel - Update widget extension to use SwiftData - Remove old Core Data files (Persistence*.swift, .xcdatamodeld) - Add EntryType enum with all entry type cases - Fix widget label truncation with proper spacing and text scaling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
import SwiftData
|
||||
import Charts
|
||||
|
||||
struct DayViewConstants {
|
||||
@@ -15,8 +15,6 @@ struct DayViewConstants {
|
||||
}
|
||||
|
||||
struct DayView: View {
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
|
||||
@AppStorage(UserDefaultsStore.Keys.deleteEnable.rawValue, store: GroupUserDefaults.groupDefaults) private var deleteEnabled = true
|
||||
|
||||
@AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system
|
||||
@@ -30,7 +28,7 @@ struct DayView: View {
|
||||
|
||||
// MARK: edit row properties
|
||||
@State private var showingSheet = false
|
||||
@State private var selectedEntry: MoodEntry?
|
||||
@State private var selectedEntry: MoodEntryModel?
|
||||
//
|
||||
|
||||
// MARK: ?? properties
|
||||
@@ -100,7 +98,7 @@ struct DayView: View {
|
||||
}
|
||||
.padding([.leading, .trailing])
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
|
||||
PersistenceController.shared.fillInMissingDates()
|
||||
DataController.shared.fillInMissingDates()
|
||||
viewModel.updateData()
|
||||
}
|
||||
.background(
|
||||
@@ -164,13 +162,13 @@ extension DayView {
|
||||
.background(.ultraThinMaterial)
|
||||
}
|
||||
|
||||
private func monthListView(month: Int, year: Int, entries: [MoodEntry]) -> some View {
|
||||
private func monthListView(month: Int, year: Int, entries: [MoodEntryModel]) -> some View {
|
||||
VStack(spacing: 12) {
|
||||
// for reach all entries
|
||||
ForEach(entries.sorted(by: {
|
||||
return $0.forDate! > $1.forDate!
|
||||
return $0.forDate > $1.forDate
|
||||
}), id: \.self) { entry in
|
||||
if filteredDays.currentFilters.contains(Int(entry.weekDay)) {
|
||||
if filteredDays.currentFilters.contains(entry.weekDay) {
|
||||
EntryListView(entry: entry)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture(perform: {
|
||||
@@ -195,13 +193,14 @@ struct ViewOffsetKey: PreferenceKey {
|
||||
|
||||
struct DayView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
DayView(viewModel: DayViewViewModel(addMonthStartWeekdayPadding: false)).environment(\.managedObjectContext, PersistenceController.shared.viewContext)
|
||||
DayView(viewModel: DayViewViewModel(addMonthStartWeekdayPadding: false))
|
||||
.modelContainer(DataController.shared.container)
|
||||
.onAppear(perform: {
|
||||
PersistenceController.shared.populateMemory()
|
||||
DataController.shared.populateMemory()
|
||||
})
|
||||
|
||||
DayView(viewModel: DayViewViewModel(addMonthStartWeekdayPadding: false))
|
||||
.preferredColorScheme(.dark)
|
||||
.environment(\.managedObjectContext, PersistenceController.shared.viewContext)
|
||||
.modelContainer(DataController.shared.container)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,18 +6,19 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
import SwiftData
|
||||
|
||||
@MainActor
|
||||
class DayViewViewModel: ObservableObject {
|
||||
@Published var grouped = [Int: [Int: [MoodEntry]]]()
|
||||
@Published var grouped = [Int: [Int: [MoodEntryModel]]]()
|
||||
@Published var numberOfItems = 0
|
||||
|
||||
|
||||
let addMonthStartWeekdayPadding: Bool
|
||||
|
||||
|
||||
var hasNoData: Bool {
|
||||
grouped.isEmpty
|
||||
}
|
||||
|
||||
|
||||
private var numberOfEntries: Int {
|
||||
var num = 0
|
||||
grouped.keys.forEach({
|
||||
@@ -28,31 +29,25 @@ class DayViewViewModel: ObservableObject {
|
||||
})
|
||||
})
|
||||
return num
|
||||
|
||||
// grouped.keys.map{
|
||||
// grouped[$0]!.values.reduce(0) { sum, array in
|
||||
// sum + array.count
|
||||
// }
|
||||
// }.reduce(0, +)
|
||||
}
|
||||
|
||||
|
||||
init(addMonthStartWeekdayPadding: Bool) {
|
||||
self.addMonthStartWeekdayPadding = addMonthStartWeekdayPadding
|
||||
|
||||
PersistenceController.shared.switchContainerListeners.append {
|
||||
|
||||
DataController.shared.switchContainerListeners.append {
|
||||
self.getGroupedData(addMonthStartWeekdayPadding: self.addMonthStartWeekdayPadding)
|
||||
}
|
||||
|
||||
PersistenceController.shared.addNewDataListener {
|
||||
DataController.shared.addNewDataListener {
|
||||
withAnimation{
|
||||
self.updateData()
|
||||
}
|
||||
}
|
||||
updateData()
|
||||
}
|
||||
|
||||
|
||||
private func getGroupedData(addMonthStartWeekdayPadding: Bool) {
|
||||
var newStuff = PersistenceController.shared.splitIntoYearMonth(includedDays: [1,2,3,4,5,6,7])
|
||||
var newStuff = DataController.shared.splitIntoYearMonth(includedDays: [1,2,3,4,5,6,7])
|
||||
if addMonthStartWeekdayPadding {
|
||||
newStuff = MoodEntryFunctions.padMoodEntriesForCalendar(entries: newStuff)
|
||||
}
|
||||
@@ -63,65 +58,55 @@ class DayViewViewModel: ObservableObject {
|
||||
public func updateData() {
|
||||
getGroupedData(addMonthStartWeekdayPadding: self.addMonthStartWeekdayPadding)
|
||||
}
|
||||
|
||||
|
||||
public func add(mood: Mood, forDate date: Date, entryType: EntryType) {
|
||||
PersistenceController.shared.add(mood: mood, forDate: date, entryType: entryType)
|
||||
DataController.shared.add(mood: mood, forDate: date, entryType: entryType)
|
||||
}
|
||||
|
||||
public func update(entry: MoodEntry, toMood mood: Mood) {
|
||||
if !PersistenceController.shared.update(entryDate: entry.forDate!, withModd: mood) {
|
||||
|
||||
public func update(entry: MoodEntryModel, toMood mood: Mood) {
|
||||
if !DataController.shared.update(entryDate: entry.forDate, withMood: mood) {
|
||||
#warning("show error")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func delete(offsets: IndexSet, inMonth month: Int, inYear year: Int) {
|
||||
if let monthEntries = grouped[year],
|
||||
let entries = monthEntries[month] {
|
||||
var mutableEntries = entries.sorted(by: {
|
||||
$0.forDate! > $1.forDate!
|
||||
$0.forDate > $1.forDate
|
||||
})
|
||||
var entriesToDelete = [MoodEntry]()
|
||||
var entriesToDelete = [MoodEntryModel]()
|
||||
for idx in offsets {
|
||||
let obj = mutableEntries.remove(at: idx)
|
||||
entriesToDelete.append(obj)
|
||||
}
|
||||
entriesToDelete.forEach({ entry in
|
||||
let entryDate = entry.forDate!
|
||||
PersistenceController.shared.viewContext.delete(entry)
|
||||
let entryDate = entry.forDate
|
||||
DataController.shared.modelContext.delete(entry)
|
||||
self.add(mood: .missing, forDate: entryDate, entryType: .listView)
|
||||
})
|
||||
}
|
||||
|
||||
do {
|
||||
try PersistenceController.shared.viewContext.save()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
}
|
||||
|
||||
DataController.shared.save()
|
||||
}
|
||||
|
||||
static func updateTitleHeader(forEntry entry: MoodEntry?) -> String {
|
||||
|
||||
static func updateTitleHeader(forEntry entry: MoodEntryModel?) -> String {
|
||||
guard let entry = entry else {
|
||||
return ""
|
||||
}
|
||||
|
||||
guard let forDate = entry.forDate else {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
let forDate = entry.forDate
|
||||
|
||||
let components = Calendar.current.dateComponents([.day, .month, .year], from: forDate)
|
||||
// let day = components.day!
|
||||
let month = components.month!
|
||||
let year = components.year!
|
||||
|
||||
|
||||
let monthName = Random.monthName(fromMonthInt: month)
|
||||
let weekday = Random.weekdayName(fromDate:entry.forDate!)
|
||||
let dayz = Random.dayFormat(fromDate:entry.forDate!)
|
||||
|
||||
let weekday = Random.weekdayName(fromDate: entry.forDate)
|
||||
let dayz = Random.dayFormat(fromDate: entry.forDate)
|
||||
|
||||
let string = weekday + " " + monthName + " " + dayz + " " + String(year)
|
||||
|
||||
|
||||
return String(format: String(localized: "content_view_fill_in_missing_entry"), string)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user