Core data SwiftUI预览画布和核心数据
预览画布正在崩溃,但在模拟器中一切正常。我假设它与@ObservedObject和@Fetchrequest有关 这里的尝试解决方案 不起作用Core data SwiftUI预览画布和核心数据,core-data,swiftui,Core Data,Swiftui,预览画布正在崩溃,但在模拟器中一切正常。我假设它与@ObservedObject和@Fetchrequest有关 这里的尝试解决方案 不起作用 import SwiftUI import CoreData struct TemplateEditor: View { @Environment(\.managedObjectContext) var managedObjectContext @FetchRequest( entity: GlobalPlaceholders.enti
import SwiftUI
import CoreData
struct TemplateEditor: View {
@Environment(\.managedObjectContext) var managedObjectContext
@FetchRequest(
entity: GlobalPlaceholders.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \GlobalPlaceholders.category, ascending: false),
]
) var placeholders: FetchedResults<GlobalPlaceholders>
@ObservedObject var documentTemplate: Templates
@State private var documentTemplateDraft = DocumentTemplateDraft()
@Binding var editing: Bool
var body: some View {
VStack(){
HStack(){
cancelButton
Spacer()
saveButton
}.padding()
addButton
ForEach(placeholders) {placeholder in
Text(placeholder.name)
}
TextField("Title", text: $documentTemplateDraft.title)
TextField("Body", text: $documentTemplateDraft.body)
.padding()
.frame(width: 100, height:400)
Spacer()
}
...
}
struct TemplateEditor_Previews: PreviewProvider {
static var previews: some View {
let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Templates")
request.sortDescriptors = [NSSortDescriptor(keyPath: \Templates.created, ascending: false)]
let documentTemplate = try! managedObjectContext.fetch(request).first as! Templates
return TemplateEditor(documentTemplate: documentTemplate, editing: .constant(true)).environment(\.managedObjectContext, managedObjectContext).environmentObject(documentTemplate)
}
}
导入快捷界面
导入CoreData
结构模板编辑器:视图{
@环境(\.managedObjectContext)变量managedObjectContext
@获取请求(
实体:GlobalPlaceholders.entity(),
sortDescriptors:[
NSSortDescriptor(键路径:\GlobalPlaceholders.category,升序:false),
]
)变量占位符:FetchedResults
@ObservedObject变量文档模板:模板
@国家私有变量documentTemplateDraft=documentTemplateDraft()
@绑定变量编辑:Bool
var body:一些观点{
VStack(){
HStack(){
取消按钮
垫片()
保存按钮
}.padding()
添加按钮
ForEach(占位符){中的占位符
文本(占位符.名称)
}
TextField(“Title”,text:$documentTemplateDraft.Title)
TextField(“Body”,text:$documentTemplateDraft.Body)
.padding()
.框架(宽:100,高:400)
垫片()
}
...
}
结构模板编辑器\u预览:PreviewProvider{
静态var预览:一些视图{
让managedObjectContext=(UIApplication.shared.delegate为!AppDelegate)。persistentContainer.viewContext
let request=NSFetchRequest(entityName:“模板”)
request.sortDescriptors=[NSSortDescriptor(keyPath:\Templates.created,升序:false)]
让documentTemplate=try!managedObjectContext.fetch(请求)。首先作为!模板
返回TemplateEditor(documentTemplate:documentTemplate,编辑:。常量(true)).environment(\.managedObjectContext,managedObjectContext)。environmentObject(documentTemplate)
}
}
预计将生成预览一个选项是不在预览中使用核心数据。这足以帮助查看我正在构建的UI,但我仍需要使用模拟器来测试功能
#if !DEBUG
// Core Data related code e.g. @FetchRequest
#endif
Xcode 11.0版(11A419c)Mac OS 10.15 Beta版(19A558d)中的建议对我有效。我的崩溃日志显示索引错误
***由于未捕获异常“NSRangeException”而终止应用程序,原因:“***-[\uu NSArray0 objectAtIndex:]:索引0超出空NSArray的界限”
因为那里没有数据,所以我不得不处理这个独特的“预览”案例,这让事情正常进行。所以,如果你在预览中的一个appear处理程序中放入一些代码,它将在启动时运行。它甚至会在你键入时实时更新
struct TemplateEditor_Previews: PreviewProvider {
static var previews: some View {
TemplateEditor().environment(\.managedObjectContext, AppDelegate.viewContext).onAppear {
let entity = GlobalPlaceholders(context: AppDelegate.viewContext)
entity.name = "abc123"
// Or create more, if you need more example data
try! AppDelegate.viewContext.save()
}
}
}
请注意,我已经在AppDelegate
上用静态方法将我的viewContext
包装起来,以使访问稍微不冗长,更容易记住:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
static var persistentContainer: NSPersistentContainer {
return (UIApplication.shared.delegate as! AppDelegate).persistentContainer
}
static var viewContext: NSManagedObjectContext {
return persistentContainer.viewContext
}
如果没有数据,我不确定您的试用线路是否有效
let documentTemplate = try! managedObjectContext.fetch(request).first as! Templates
为了让我的工作正常,我创建了一个要使用的测试项。如下所示:
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
//Test data
let newEvent = Event.init(context: context)
newEvent.timestamp = Date()
return DetailView(event: newEvent).environment(\.managedObjectContext, context)
}
}
我还注意到我需要.environment(.managedObjectContext,context)托管CoreData视图或预览的早期选项卡视图中的代码将失败。这个答案在我最近的项目中似乎有效,它替换了默认的ContentView\u Previews结构,尽管其他人质疑它是否会提取持久数据。这要归功于Xcode Beta 7中主/详细模板项目中的@ShadowDES- 我可以使用Canvas(XCode版本11.3(11C29))对任何东西进行CRUD,而且它似乎运行得完美无缺
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return ContentView().environment(\.managedObjectContext, context)
}
}
#endif
适用于使用
应用程序
模板的SwiftUI 2应用程序
我还遇到了预览崩溃,其他解决方案都不适合我,也不适合我
我所做的不是以下几点:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
return ContentView()
.environment(
\.managedObjectContext,
CoreDataManager.context
)
}
}
我用以下方法修复了它:
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let context = CoreDataManager.context
/* Optional sample data can be inserted here */
return ContentView()
.environment(
\.managedObjectContext,
context
)
}
}
其中CoreDataManager
是:
enum CoreDataManager {
static var context: NSManagedObjectContext {
persistentContainer.viewContext
}
static let persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "MyContainerName")
container.loadPersistentStores { description, error in
guard let error = error else { return }
fatalError("Core Data error: '\(error.localizedDescription)'.")
}
return container
}()
}
不清楚这有什么帮助,但现在它工作得很好。此外,您可以将示例数据添加到我用注释标记的上下文中。这是我的解决方案
我不想在视图中使用CoreData。我想要MVVM样式。
所以您需要模拟核心数据,以便在画布视图中显示
这是一个例子:
// View
struct MyView: View {
@ObservedObject var viewModel: PreviewViewModel
}
// View Model
final class MyViewModel: ObservableObject {
@Published var repository: RepositoryProtocol // CoreData
}
// Repository
protocol RepositoryProtocol { }
class Repository: RepositoryProtocol { ... }
class MockRepository: RepositoryProtocol { ... } // Create a Mock
// Init of your view
// If Canvas use mock
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
repository = MockRepository()
// else App use Repository
} else {
repository = Repository.shared
}
let viewModel = MyViewModel(repository:repository)
MyViewModel(viewModel: viewModel)
对我有用的是:
我在persistence controller的preview属性中创建了我的所有示例数据,在启动项目时使用以下设置生成Xcode模板:Interface-SwiftUI、Lifecycle-SwiftUI App、Use Core data、Host in CloudKit。我已将模板发布在此处:
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
static var preview: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
// ** Prepare all sample data for previews here ** //
for _ in 0..<10 {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
}
do {
try viewContext.save()
} catch {
// handle error for production
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
return result
}()
let container: NSPersistentCloudKitContainer
init(inMemory: Bool = false) {
container = NSPersistentCloudKitContainer(name: "SwiftUISwiftAppCoreDataCloudKit")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// handle error for production
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}
它崩溃是因为在PersistenceController
中指示它这样做:
struct PersistenceController{
...
静态变量预览:PersistenceController={
...
做{
请尝试viewContext.save()
}抓住{
//将此实现替换为适当处理错误的代码。
//fatalError()导致应用程序生成崩溃日志并终止。您不应在装运应用程序中使用此函数,尽管它在开发过程中可能很有用。
设nsError=错误为nsError
fatalError(“未解决的错误\(nsError),\(nsError.userInfo)”)
}
返回结果
}()
...
}
因此,实际原因可以在崩溃报告中看到。实际上,XCode 12.4显示了关于检查崩溃报告的警告;但是,对于像我这样的web开发新手来说,报告过于冗长。因此,我花了一段时间才发现问题,所以我希望这能为其他人节省一些时间
…在我的例子中,问题是在为预览填充核心数据模型时未设置必需的属性。这对我很有效。在AppDelegate中,创建不同的预览上下文并用对象填充它
lazy var persistentContainerPreview: NSPersistentContainer = {
let persistentContainer = NSPersistentContainer(name: "MyModel")
persistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
}
})
let didCreateSampleData = UserDefaults.standard.bool(forKey: "didCreateSampleData")
if !didCreateSampleData {
let context = persistentContainer.viewContext
let recipe = Recipe(context: context)
recipe.title = "Soup 2"
recipe.difficultyName = "NOT TOO TRICKY"
recipe.difficultyValue = 1
recipe.heroImage = "dsfsdf"
recipe.ingredients = "meat"
recipe.method = "sdcsdsd"
recipe.published = Date()
recipe.recipeId = 1
recipe.servings = 4
recipe.tags = "sdfs"
recipe.totalTime = 100
recipe.totalTimeFormatted = "Less than 2 hours"
try! context.save()
}
return persistentContainer
}()
然后在预览中
struct RecipeView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainerPreview.viewContext
let recipe = try! context.fetch(Recipe.fetchRequest()).first as! Recipe
RecipeView(recipe: recipe).environment(\.managedObjectContext, context)
}
}问题是,您需要找出是哪条线路导致了崩溃
因为画布没有显示详细的错误,所以使用OSLog和Console.app进行调试将是一个可能的解决方案
例如:
import os.log
struct YourView_Previews: PreviewProvider {
static var previews: some View {
os_log("[DEBUG]-\(#function)---1--")
let moc = PersistenceController.preview.container.viewContext
os_log("[DEBUG]-\(#function)---2--")
let item = Item.previewData(context: moc)
os_log("[DEBUG]-\(#function)---3--")
return YourView(item: item, now: Date())
.environment(\.managedObjectContext, moc)
}
}
记住使用过滤器更好地捕获来自的调试消息
import os.log
struct YourView_Previews: PreviewProvider {
static var previews: some View {
os_log("[DEBUG]-\(#function)---1--")
let moc = PersistenceController.preview.container.viewContext
os_log("[DEBUG]-\(#function)---2--")
let item = Item.previewData(context: moc)
os_log("[DEBUG]-\(#function)---3--")
return YourView(item: item, now: Date())
.environment(\.managedObjectContext, moc)
}
}