Swift 和 SwiftUI 之间的 viewContext 是不是不同
Posted
技术标签:
【中文标题】Swift 和 SwiftUI 之间的 viewContext 是不是不同【英文标题】:Is viewContext different between Swift and SwiftUISwift 和 SwiftUI 之间的 viewContext 是否不同 【发布时间】:2021-01-24 01:41:01 【问题描述】:编辑:我在底部添加了我的问题的改写。
所以我有一个我已经开发了很长时间的应用程序。我用它来自学 Xcode / Swift,现在我又拥有一台 Mac,我正在尝试通过将应用程序完全重新构建为一个新项目来学习 SwiftUI。
我在 CoreData 中有一个实体,它只包含 1 条记录,它具有我正在跟踪的设置的所有属性。
之前,我可以对我的实体进行扩展,Ent_Settings
,这样我就可以始终取回该特定记录。
class var returnSettings: Ent_Settings
//this is inside of extension Ent_Settings
let moc = PersistenceController.shared.container.viewContext
var settings: Ent_Settings?
let fetchRequest: NSFetchRequest<Ent_Settings> = Ent_Settings.fetchRequest()
do
let results = try moc.fetch(fetchRequest)
if results.count == 0
Ent_Settings.createSettingsEntity()
do
let results = try moc.fetch(fetchRequest)
settings = results.first!
catch
error.tryError(tryMessage: "Failed performing a fetch to get the Settings object after it was created.", loc: "Ent_Settings Extension")
else
settings = results.first!
catch
error.tryError(tryMessage: "Fetching Settings Object", loc: "Ent_Settings Extension")
return settings!
由于没有AppDelegate
,我花了一段时间才弄清楚如何获取 moc,但您可以看到我在代码中找到了并且看起来效果很好。
但是,当我转到我在 SwiftUI 中创建的视图时,我得到了奇怪的结果。
在模拟器中,我可以运行我的应用程序并查看更改。离开视图并返回并查看更改已保存。但是,如果我重建并运行应用程序,它会丢失所有数据。所以它似乎根本不记得事情。基本上,它是易失性内存而不是持久性内存。但是如果我在Ent_Settings
的扩展中添加另一个函数来更新该属性,然后再次构建并运行,它将永久保存。
我想知道它是否与上下文的管理方式有关,也许它没有看到变化,因为它是一个不同的“侦听器”?
这是我在视图中所做的更改。 (我试着去掉那些让它更难看到的多余的东西)
import SwiftUI
struct Settings_CheckedView: View
@Environment(\.managedObjectContext) private var viewContext
@State private var picker1 = 0
var body: some View
Form
Section(header: Text("..."),
footer: Text("..."))
VStack
HStack ...
Picker("", selection: $picker1)
Text("Off").tag(0)
Text("Immediately").tag(1)
Text("Delay").tag(2)
.pickerStyle(SegmentedPickerStyle())
.onChange(of: picker1, perform: value in
settings.autoDeleteSegment = Int16(value)
viewContext.trySave("Updating auto-delete segment value in Settings",
loc: "Ent_Settings Extension")
)
...
...
.onAppear(perform:
settings = Ent_Settings.returnSettings
self.picker1 = Int(settings.autoDeleteSegment)
self.picker2 = Int(settings.deleteDelaySegment)
)
这是我用于trySave()
函数的代码,我在viewContext
上拥有
extension NSManagedObjectContext
func trySave(_ tryMessage: String, loc: String)
let context = self
if context.hasChanges
do
try self.save()
catch
print("Attempted: \(tryMessage) -in \(loc)")
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
此代码似乎实际上并未将任何内容保存到数据库中。
我还尝试将此代码添加到 Settings_CheckedView
中,看看是否会产生影响,但我得到了奇怪的结果。
@FetchRequest(sortDescriptors:[])
private var settingsResult: FetchedResults<Ent_Settings>
但即使我知道 Ent_Settings
有 1 条记录,它也会返回零结果。所以这让我想知道我是否真的按照我的方式创建了两个数据库。
我对 SwiftUI 真的很陌生,CoreData 似乎是那些很难获得信息的东西之一。希望有人能指出我哪里出错了。我希望能够在 Swift 和 SwiftUI 中对 CoreData 进行更改。
感谢您提供的任何帮助,如果您需要添加任何其他内容,请告诉我。
已编辑:尝试改写问题
所以最终,我想在该实体的扩展中使用 CoreData 做一些事情。我的数据库有很多方面,但我还在其中创建了一个实体来存储设置信息,并且由于该实体与其他任何内容都不相关,我认为这将是学习 SwiftUI 的一个很好的起点。我可以在 UIKit 中使用所有东西,但我也特别想学习 SwiftUI。
根据我对 SwiftUI 的理解,它旨在替换故事板并使生命周期变得更加容易。所以我处理 CoreData 的类仍然应该在 SwiftUI 外部执行。
正如您在上面的Settings_CheckedView
视图中所见,我从Ent_Settings
的扩展中引用了该单个设置记录。基本上,该扩展程序中的所有内容都负责返回设置的单个记录,检查该记录是否存在,如果不存在则创建它(基本上是第一次运行应用程序)
理想情况下,我希望将功能保留在 Ent_Settings 的扩展中,但我的问题是我无法获得正确的 moc 实例来持久保存它。
@main
struct MyApp: App
let persistenceController = PersistenceController.shared
var body: some Scene
WindowGroup
MainMenuView().onAppear(perform:
if Ent_Lists.hasAnyList(persistenceController.container.viewContext) == false
Ent_Lists.createAnyList(persistenceController.container.viewContext)
if Ent_Section.hasBlankSection(persistenceController.container.viewContext) == false
Ent_Section.createBlankSection(persistenceController.container.viewContext)
//self.settings = Ent_Settings.returnSettings(persistenceController.container.viewContext)
)
//ContentView()
//.environment(\.managedObjectContext, persistenceController.container.viewContext)
我很确定let persistenceController = PersistenceController.shared
正在初始化整个应用程序中使用的持久控制器。创建或检索它。
所以,我首先在 Ent_Settings 中尝试了这个:
let moc = PersistenceController.shared.container.viewContext
但我认为这可能是在 MyApp
内部创建的 PersistenceController 之外创建一个新的 PersistenceController
我也尝试了let moc = EZ_Lists_SwiftUI.PersistenceController.shared.container.viewContext
,但我很确定这也会创建一个新实例,因为我只能访问大写的 PersistenceController 而不是小写的。
我也尝试像这样从视图中传递 moc:class func createSettingsEntity(_ moc: NSManagedObjectContext)
但我收到关于 moc 为 nil 的错误,所以我猜 moc 不能通过引用从 Struct 发送。
线程 1:“+entityForName: nil 不是用于搜索实体名称 'Ent_Settings' 的合法 NSPersistentStoreCoordinator”
为了清楚起见,我将其添加到视图中:@Environment(\.managedObjectContext) private var viewContext
将此 @State 属性添加到视图中:@State private var settings: Ent_Settings!
并将其设置在 .onAppear
中,其中:self.settings = Ent_Settings.returnSettings(self.viewContext)
所以我真正在寻找的是如何从Ent_Settings
的扩展中访问 moc。在我纯粹是 UIKit 和 Storyboards 的应用程序中,我使用了这个:let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
,但由于不再有 AppDelegate,我不能再使用它了。当您在使用 SwiftUI 但在视图之外工作时(例如在课堂上或像我一样的东西中),用什么代替它来获得 moc?谢谢你。希望这些额外信息有助于解释我的问题。
【问题讨论】:
【参考方案1】:如果您只使用 coredata 来存储设置,我会查看 UserDefaults
以及是否符合您的用例。
https://developer.apple.com/documentation/foundation/userdefaults
如果您出于某种原因需要使用 CoreData,我建议您启用 CloudKit,因为 CloudKit 将允许您通过 Web 控制台实际查看保存的数据。
此外,SwiftUI 不会为您创建 AppDelegate,您仍然可以像使用 UIKit 一样创建自己的 AppDelegate.swift 文件。 只需在结构声明之前将@UIApplicationDelegateAdaptor 定义到您最顶层结构(带有@main 的那个)中的自定义AppDelegate.swift 文件中。
@main
struct MainAppView: App
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
【讨论】:
感谢您的推荐。实际上,我在那里有很多东西作为相关数据库,只是想轻松开始。不过,我会尝试按照您推荐的方式访问 appDelegate,谢谢。 我正在进一步阅读您的帖子,看起来您正在给我代码以在 SwiftUI 中创建 AppDelegate。但我实际上想要相反的。我正在查看您如何使用@Environment 在 SwiftUI 中获取 moc,但是如果我正在创建一个也需要 moc 的类,那么我该如何获取它,因为它在 SwiftUI 之外。这就是我提到 AppDelegate 的原因,因为在 SwiftUI 出现之前我就是这样获得它的。 我更新了我的问题,希望它能让我更清楚我在寻找什么。【参考方案2】:好吧,我脸上有点鸡蛋。我终于明白了为什么我会遇到这么多麻烦。当我在做我的应用程序时,我把所有与苹果提供的 ContextView 相关的东西都注释掉了,注释掉的太多了。
//ContentView()
//.environment(\.managedObjectContext, persistenceController.container.viewContext)
我丢失了设置环境对象的部分。把它放回去后,我现在可以保存到 moc 中了。
在我的实体类扩展中,我以这种方式访问 moc:
let moc = MyApp.PersistenceController.shared.container.viewContext
感谢所有查看并尝试提供帮助的人。原来我是在开枪打自己的脚。
【讨论】:
以上是关于Swift 和 SwiftUI 之间的 viewContext 是不是不同的主要内容,如果未能解决你的问题,请参考以下文章
Swift之深入解析如何结合Core Data和SwiftUI