我有一個提醒應用程式,我正在嘗試在其中實作持久資料,但每當我關閉應用程式時,都不會保存任何資料。我知道如何使它與普通的 MVC 一起作業,但我想讓它與我擁有的視圖模型一起作業。
我想我知道需要改變什么來解決問題,但我不確定如何找到解決方案。我很確定在 NavigationView 下的 ReminderApp 檔案中顯示 HomeViewModel(reminds: store.reminds) 我認為 store.reminds 部分需要在開始時與 $ 系結但是當我嘗試這樣做時它不會'不起作用,而是說 HomeViewModel 提醒屬性需要提醒而不是系結。
ReminderStore 加載提醒并將提醒保存到帶有提醒的檔案中,HomeViewModel 包含提醒陣列,并在用戶添加新提醒時將提醒附加到陣列中。
如果有人知道如何讓這個作業,那就太好了,因為我一直堅持這一點。我的最小可重現示例代碼如下。
提醒應用
import SwiftUI
@main
struct RemindersApp: App {
@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)
}
}
}
.navigationBarHidden(true)
}
.onAppear {
ReminderStore.load { result in
switch result {
case .failure(let error):
fatalError(error.localizedDescription)
case .success(let reminds):
store.reminds = reminds
}
}
}
}
}
}
主頁查看
import SwiftUI
struct HomeView: View {
@StateObject var homeVM: HomeViewModel
@Environment(\.scenePhase) private var scenePhase
@State var addView = false
let saveAction: ()->Void
var body: some View {
VStack {
List {
ForEach($homeVM.reminds) { $remind in
Text(remind.title)
}
}
}
.safeAreaInset(edge: .top) {
HStack {
Text("Reminders")
.font(.title)
.padding()
Spacer()
Button(action: {
addView.toggle()
}) {
Image(systemName: "plus")
.padding()
.font(.title2)
}
.sheet(isPresented: $addView) {
NavigationView {
VStack {
Form {
TextField("Title", text: $homeVM.newRemindData.title)
}
}
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Dismiss") {
homeVM.newRemindData = Reminder.Data()
addView.toggle()
}
}
ToolbarItem(placement: .principal) {
Text("New Reminder")
.font(.title3)
}
ToolbarItem(placement: .confirmationAction) {
Button("Add") {
homeVM.addRemindData(remindData: homeVM.newRemindData)
addView.toggle()
}
}
}
}
}
.onChange(of: scenePhase) { phase in
if phase == .inactive { saveAction() }
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
HomeView(homeVM: HomeViewModel(reminds: Reminder.sampleReminders), saveAction: {})
}
}
提醒商店
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 reminds = try JSONDecoder().decode([Reminder].self, from: file.availableData)
DispatchQueue.main.async {
completion(.success(reminds))
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
}
}
static func save(reminds: [Reminder], completion: @escaping (Result<Int, Error>) -> Void) {
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))
}
}
}
}
主頁查看模型
import Foundation
class HomeViewModel: ObservableObject {
@Published var reminds: [Reminder]
@Published var newRemindData = Reminder.Data()
init(reminds: [Reminder]) {
self.reminds = reminds
}
func addRemindData(remindData: Reminder.Data) {
let newRemind = Reminder(data: remindData)
reminds.append(newRemind)
newRemindData = Reminder.Data()
}
}
提醒
import Foundation
struct Reminder: Identifiable, Codable {
var title: String
let id: UUID
init(title: String, id: UUID = UUID()) {
self.title = title
self.id = id
}
}
extension Reminder {
struct Data {
var title: String = ""
var id: UUID = UUID()
}
var data: Data {
Data(title: title)
}
mutating func update(from data: Data) {
title = data.title
}
init(data: Data) {
title = data.title
id = UUID()
}
}
extension Reminder {
static var sampleReminders = [
Reminder(title: "Reminder1"),
Reminder(title: "Reminder2"),
Reminder(title: "Reminder3")
]
}
uj5u.com熱心網友回復:
您在這里苦苦掙扎的原因是因為您嘗試擁有多個Source of truth.
SwiftUI 中的資料流檔案
您應該將代碼從您的代碼HomeViewModel移至您的ReminderStore并將static函式更改為實體函式。這會將您的邏輯保留在一個地方。
您可以將您ReminderStore的HomeView作為 @EnvironmentObject
這會將您的代碼簡化為:
class ReminderStore: ObservableObject {
@Published var reminds: [Reminder] = []
@Published var newRemindData = Reminder.Data()
private func fileURL() throws -> URL {
try FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
.appendingPathComponent("reminds.data")
}
func load() {
DispatchQueue.global(qos: .background).async {
do {
let fileURL = try self.fileURL()
guard let file = try? FileHandle(forReadingFrom: fileURL) else {
return
}
let reminds = try JSONDecoder().decode([Reminder].self, from: file.availableData)
DispatchQueue.main.async {
self.reminds = reminds
}
} catch {
DispatchQueue.main.async {
fatalError(error.localizedDescription)
}
}
}
}
func save() {
do {
let data = try JSONEncoder().encode(reminds)
let outfile = try fileURL()
try data.write(to: outfile)
} catch {
fatalError(error.localizedDescription)
}
}
func addRemindData() {
let newRemind = Reminder(data: newRemindData)
reminds.append(newRemind)
newRemindData = Reminder.Data()
}
}
struct RemindersApp: App {
@StateObject private var store = ReminderStore()
var body: some Scene {
WindowGroup {
NavigationView {
HomeView() {
store.save()
}
.navigationBarHidden(true)
.environmentObject(store)
}
.onAppear {
store.load()
}
}
}
}
struct HomeView: View {
@Environment(\.scenePhase) private var scenePhase
@EnvironmentObject private var store: ReminderStore
@State var addView = false
let saveAction: ()->Void
var body: some View {
VStack {
List {
ForEach(store.reminds) { remind in
Text(remind.title)
}
}
}
.safeAreaInset(edge: .top) {
HStack {
Text("Reminders")
.font(.title)
.padding()
Spacer()
Button(action: {
addView.toggle()
}) {
Image(systemName: "plus")
.padding()
.font(.title2)
}
.sheet(isPresented: $addView) {
NavigationView {
VStack {
Form {
TextField("Title", text: $store.newRemindData.title)
}
}
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Dismiss") {
store.newRemindData = Reminder.Data()
addView.toggle()
}
}
ToolbarItem(placement: .principal) {
Text("New Reminder")
.font(.title3)
}
ToolbarItem(placement: .confirmationAction) {
Button("Add") {
store.addRemindData()
addView.toggle()
}
}
}
}
}
.onChange(of: scenePhase) { phase in
if phase == .inactive { saveAction() }
}
}
}
}
}
我建議解決的一個問題:用已經被采用的東西命名一個型別Swift是個壞主意。您應該將您的Data結構重命名為不同的名稱。
uj5u.com熱心網友回復:
ReminderStore.save 沒有及時呼叫。
到它呼叫時,它沒有/獲取提醒資料。
這是我要確保完成的第一件事。之后您可能最終會遇到其他問題,但我個人會首先關注這一點。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/487356.html
