[ad_1]
I tried to implement persistent data in my app but when I close out my app and re open it none of the reminders that were created were saved. Does anyone know how I would be able to fix this. I have my ReminderStore, HomeView, NextUpApp, and HomeViewModel listed below. at the bottom of HomeView there is an onChange modifier so the app knows to save the users actions(reminders) when the app is inactive.
Reminder
”’
struct Reminder: Identifiable, Equatable, Codable {
var title: String
var notes: String?
var date: Date?
var time: Date?
var theme: Theme
var iscomplete: Bool
var priority: RemindPriority
let id: UUID
//let filter: FilterType
init(title: String, notes: String? = nil, date: Date? = nil, time: Date? = nil, theme: Theme, iscomplete: Bool = false, priority: RemindPriority = .None, id: UUID = UUID()) {
self.title = title
self.notes = notes
self.date = date
self.time = time
self.theme = theme
self.iscomplete = iscomplete
self.priority = priority
self.id = id
}
”’
ReminderStore
”’
import Foundation
import SwiftUI
class ReminderStore: ObservableObject {
@Published var reminds: [Reminder] = []
private static func fileURL() throws -> URL {
try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("reminds.data")
}
static func load(completion: @escaping (Result<[Reminder], Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let fileURL = try fileURL()
guard let file = try? FileHandle(forReadingFrom: fileURL) else {
DispatchQueue.main.async {
completion(.success([]))
}
return
}
let reminders = try JSONDecoder().decode([Reminder].self, from: file.availableData)
DispatchQueue.main.async {
completion(.success(reminders))
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
static func save(reminds: [Reminder], completion: @escaping (Result<Int, Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let data = try JSONEncoder().encode(reminds)
let outfile = try fileURL()
try data.write(to: outfile)
DispatchQueue.main.async {
completion(.success(reminds.count))
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
}
”’
HomeView
”’
import SwiftUI
struct HomeView: View {
@StateObject var homeVM: HomeViewModel
@Environment(\.scenePhase) private var scenePhase
@State var percent: Int = 1
@State var showDetailEditView = false
@State var showAddView = false
@State var dropDown = false
@State var index: Int?
@ObservedObject private var notificationManager = LocalNotificationManager()
let saveAction: ()->Void
//@State var filter = false
var body: some View {
ZStack {
List {
ForEach($homeVM.reminds) { $remind in
NavigationLink(destination: ReminderDetailView(remindVM: ReminderViewModel(remind: $remind))) {
ReminderView(remind: $remind)
}
.listRowBackground(remind.theme.mainColor)
.buttonStyle(PlainButtonStyle())
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.swipeActions(edge: .leading) {
Button(action: {
self.showDetailEditView.toggle()
homeVM.existingRemindData = remind.data
index = homeVM.reminds.firstIndex(of: remind)
}) {
Label("Edit", systemImage: "pencil")
}
}
.sheet(isPresented: $showDetailEditView, onDismiss: {homeVM.existingRemindData = Reminder.Data()}) {
NavigationView {
ReminderEditView(data: $homeVM.existingRemindData)
.navigationTitle(homeVM.existingRemindData.title)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") {
self.showDetailEditView.toggle()
homeVM.existingRemindData = Reminder.Data()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
self.showDetailEditView.toggle()
homeVM.reminds[index!].update(from: homeVM.existingRemindData)
if homeVM.reminds.last?.data.date != nil {
notificationManager.deleteLocalNotifications(identifiers: [homeVM.reminds[index!].id.uuidString])
notificationManager.sendNotification(date: (homeVM.reminds.last?.data.combineDateWithTime(date: (homeVM.reminds.last?.data.date)!, time: homeVM.reminds.last?.data.time ?? Calendar(identifier: .gregorian).date(bySettingHour: 0, minute: 0, second: 0, of: Date())!))!, type: "date", title: (homeVM.reminds.last?.title)!, body: (homeVM.reminds.last?.notes) ?? "", id: (homeVM.reminds.last?.id)!)
print(Calendar(identifier: .gregorian).date(bySettingHour: 0, minute: 0, second: 0, of: Date())!)
//print(notificationManager.notifications)
//print((homeVM.reminds.last?.id)!)
}
homeVM.newRemindData = Reminder.Data()
}
}
}
.background(LinearGradient(gradient: Gradient(colors: [
Color(UIColor(red: 0.376, green: 0.627, blue: 0.420, alpha: 1)),
Color(UIColor(red: 0.722, green: 0.808, blue: 0.725, alpha: 1))
]), startPoint: .topLeading, endPoint: .bottomTrailing))
}
}
.swipeActions(allowsFullSwipe: true) {
Button (role: .destructive, action: {
homeVM.deleteReminder(remind: remind)
notificationManager.deleteLocalNotifications(identifiers: [remind.id.uuidString])
print("\(notificationManager.deletedNotifications.count) 3")
}) {
Label("Delete", systemImage: "trash.fill")
}
}
}
}
.onAppear(
perform: {
UITableView.appearance().backgroundColor = .clear
UITableViewCell.appearance().backgroundColor = .clear
})
.safeAreaInset(edge: .top) {
HStack {
/*
Button(action: {
self.filter.toggle()
}) {
Image(systemName: "line.3.horizontal")
.font(.system(size: 25))
}
Spacer()
HStack {
Text("Hello")
.font(.largeTitle.bold())
Image(systemName: "chevron.right")
.font(.system(size: 17, weight: .bold, design: .default))
.frame(width: 10, height: 10, alignment: .center)
.onTapGesture {
self.dropDown.toggle()
}
.rotationEffect(dropDown ? .degrees(90) : .degrees(0))
.animation(Animation.easeInOut(duration: 0.2), value: dropDown)
}
*/
Text("NextUp \(homeVM.reminds.count)")
.font(.largeTitle.bold())
Spacer()
Button(action: {
self.showAddView.toggle()
}) {
Image(systemName: "plus")
.font(.system(size: 25))
}
.sheet(isPresented: $showAddView, onDismiss: {homeVM.newRemindData = Reminder.Data()}) {
NavigationView {
ReminderEditView(data: $homeVM.newRemindData)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Dismiss") {
showAddView = false
homeVM.newRemindData = Reminder.Data()
}
}
ToolbarItem(placement: .confirmationAction) {
Button("Add") {
homeVM.newReminder()
if homeVM.reminds.last?.data.date != nil {
notificationManager.sendNotification(date: (homeVM.reminds.last?.data.combineDateWithTime(date: (homeVM.reminds.last?.data.date)!, time: homeVM.reminds.last?.data.time ?? Calendar(identifier: .gregorian).date(bySettingHour: 0, minute: 0, second: 0, of: Date())!))!, type: "date", title: (homeVM.reminds.last?.title)!, body: (homeVM.reminds.last?.notes) ?? "", id: (homeVM.reminds.last?.id)!)
print(Calendar(identifier: .gregorian).date(bySettingHour: 0, minute: 0, second: 0, of: Date())!)
//print(notificationManager.notifications)
//print((homeVM.reminds.last?.id)!)
}
showAddView = false
}
}
}
.background(LinearGradient(gradient: Gradient(colors: [
Color(UIColor(red: 0.376, green: 0.627, blue: 0.420, alpha: 1)),
Color(UIColor(red: 0.722, green: 0.808, blue: 0.725, alpha: 1))
]), startPoint: .topLeading, endPoint: .bottomTrailing))
.navigationTitle("New Reminder")
}
}
}
.padding()
.background(Color.clear.overlay(.ultraThickMaterial))
}
.safeAreaInset(edge: .bottom) {
ForEach(homeVM.reminds) { remind in
if remind.iscomplete {
ProgressBarView(progress: Float(homeVM.compReminds.count) / Float(homeVM.reminds.count))
.padding(.vertical)
.onAppear {
homeVM.appendRemind(complete: remind)
}
.onDisappear {
homeVM.removeRemind(remind: remind)
}
}
}
.frame(height: 75)
.background(Color.white.overlay(.ultraThickMaterial))
}
.ignoresSafeArea(edges: .bottom)
.background(LinearGradient(gradient: Gradient(colors: [
Color(UIColor(red: 0.376, green: 0.627, blue: 0.420, alpha: 1)),
Color(UIColor(red: 0.722, green: 0.808, blue: 0.725, alpha: 1))
]), startPoint: .topLeading, endPoint: .bottomTrailing)
)
.navigationBarHidden(true)
.onChange(of: scenePhase) { phase in
if phase == .inactive { saveAction() }
}
”’
NextUpApp
”’
import SwiftUI
@main
struct NextUpApp: App {
//@State private var reminds = Reminder.sampleReminders
@StateObject private var store = ReminderStore()
var body: some Scene {
WindowGroup {
NavigationView {
HomeView(homeVM: HomeViewModel(reminds: store.reminds)) {
ReminderStore.save(reminds: store.reminds) { result in
if case .failure(let error) = result {
fatalError(error.localizedDescription)
}
}
}
}
.onAppear {
ReminderStore.load { result in
switch result {
case .failure(let error):
fatalError(error.localizedDescription)
case .success(let reminds):
store.reminds = reminds
//print("Works")
}
}
}
}
}
}
”’
HomeViewModel
”’
import Foundation
import SwiftUI
import Combine
class HomeViewModel: ObservableObject {
@Published var reminds: [Reminder]
@Published var newRemindData = Reminder.Data()
@Published var existingRemindData = Reminder.Data()
@Published var selectedRemind = Reminder(data: Reminder.Data())
@Published var compReminds: [Reminder] = []
private var cancellables = Set<AnyCancellable>()
init(reminds: [Reminder]) {
self.reminds = reminds
}
func newReminder() {
let newRemind = Reminder(data: newRemindData)
reminds.append(newRemind)
newRemindData = Reminder.Data()
}
func deleteReminder(remind: Reminder) {
Just(remind)
.delay(for: .seconds(0.25), scheduler: RunLoop.main)
.sink {remind in
if remind.iscomplete {
self.removeRemind(remind: remind)
}
if !remind.iscomplete {
self.removeRemind(remind: remind)
}
self.reminds.removeAll { $0.id == remind.id }
}
.store(in: &cancellables)
}
func appendRemind(complete: Reminder) {
compReminds.append(complete)
}
func removeRemind(remind: Reminder) {
compReminds.removeAll() { $0.id == remind.id }
}
func remindIndex() -> Int {
return reminds.firstIndex(where: {
$0.id == existingRemindData.id
}) ?? 1
}
}
”’
[ad_2]
Source link