【问题标题】:Cannot preview this file, app may have crashed -- Occurs when inputting specific line of code无法预览此文件,应用程序可能已崩溃 -- 输入特定代码行时发生
【发布时间】:2020-07-15 04:43:01
【问题描述】:

虽然我在堆栈溢出上四处寻找答案,但感觉我的情况对于这个错误来说是独一无二的。

我一直在学习如何在 SwiftUI 中使用 CoreData 来获取持久数据。我首先制作了一个基本的电影列表,当您单击“添加电影”按钮时,它会添加带有任意文本的电影,重点是让它工作。

我的ContentView 中有一个列表,它在Movie 实体上执行ForEach,但是在添加这些代码行时:

            List {
                ForEach(movies, id: \.self) { (movie: Movie) in
                    Text(movie.title ?? "Unknown Movie")
                }
            }

我收到一个错误:

PotentialCrashError: MyMovieList.app may have crashed

MyMovieList.app may have crashed. Check ~/Library/Logs/DiagnosticReports for any crash logs 
from your application.

==================================

|  Error Domain=com.apple.dt.ultraviolet.service Code=12 "Rendering service was interrupted" 
UserInfo={NSLocalizedDescription=Rendering service was interrupted}

但是当我构建并运行应用程序时,它运行良好。添加该特定代码行时,似乎只有预览会中断。将其注释掉将允许预览再次工作。

我的Movie 实体仅包含一个属性title,它是一个可选的字符串。

ContentView 的完整代码:

import SwiftUI

struct ContentView: View {
// 1
@FetchRequest(
  // 2
  entity: Movie.entity(),
  // 3
  sortDescriptors: [
    NSSortDescriptor(keyPath: \Movie.title, ascending: true)
  ]
// 4
) var movies: FetchedResults<Movie>

@Environment(\.managedObjectContext) var managedObjectContext

var body: some View {
    NavigationView {
        VStack {
            Button(action: {
                self.addMovie(title: "Generic Movie")
            }) {
                Text("Add Movie")
            }
            List {
                ForEach(movies, id: \.self) { (movie: Movie) in
                    Text(movie.title ?? "Unknown Movie")
                }
            }
        }.navigationBarTitle("My Movies")
    }
}
func deleteItem(at offsets: IndexSet) {
    // 1
    offsets.forEach { index in
      // 2
      let movie = self.movies[index]
      // 3
      self.managedObjectContext.delete(movie)
    }
    // 4
    saveContext()
}
func saveContext() {
    do {
        try managedObjectContext.save()
    } catch {
        print("Error saving managed object context: \(error)")
    }
}
func addMovie(title: String) {
  // 1
  let newMovie = Movie(context: managedObjectContext)

  // 2
  newMovie.title = title

  // 3
  saveContext()
}
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

我的AppDelegateSceneDelegate 是创建项目时生成的默认值,但无论如何我都会在这里分享它们,因为我知道人们会问。

AppDelegate

import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {



func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    return true
}

// MARK: UISceneSession Lifecycle

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {
    /*
     The persistent container for the application. This implementation
     creates and returns a container, having loaded the store for the
     application to it. This property is optional since there are legitimate
     error conditions that could cause the creation of the store to fail.
    */
    let container = NSPersistentContainer(name: "MyMovieList")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            // 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.
             
            /*
             Typical reasons for an error here include:
             * The parent directory does not exist, cannot be created, or disallows writing.
             * The persistent store is not accessible, due to permissions or data protection when the device is locked.
             * The device is out of space.
             * The store could not be migrated to the current model version.
             Check the error message to determine what the actual problem was.
             */
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

// MARK: - Core Data Saving support

func saveContext () {
    let context = persistentContainer.viewContext
    if context.hasChanges {
        do {
            try context.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)")
        }
    }
}

}

SceneDelegate:

import UIKit
import SwiftUI

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?


func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

    // Get the managed object context from the shared persistent container.
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

    // Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath.
    // Add `@Environment(\.managedObjectContext)` in the views that will need the context.
    let contentView = ContentView().environment(\.managedObjectContext, context)

    // Use a UIHostingController as window root view controller.
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
}

func sceneDidDisconnect(_ scene: UIScene) {
    // Called as the scene is being released by the system.
    // This occurs shortly after the scene enters the background, or when its session is discarded.
    // Release any resources associated with this scene that can be re-created the next time the scene connects.
    // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}

func sceneDidBecomeActive(_ scene: UIScene) {
    // Called when the scene has moved from an inactive state to an active state.
    // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}

func sceneWillResignActive(_ scene: UIScene) {
    // Called when the scene will move from an active state to an inactive state.
    // This may occur due to temporary interruptions (ex. an incoming phone call).
}

func sceneWillEnterForeground(_ scene: UIScene) {
    // Called as the scene transitions from the background to the foreground.
    // Use this method to undo the changes made on entering the background.
}

func sceneDidEnterBackground(_ scene: UIScene) {
    // Called as the scene transitions from the foreground to the background.
    // Use this method to save data, release shared resources, and store enough scene-specific state information
    // to restore the scene back to its current state.

    // Save changes in the application's managed object context when the application transitions to the background.
    (UIApplication.shared.delegate as? AppDelegate)?.saveContext()
}


}

【问题讨论】:

    标签: swift xcode core-data swiftui


    【解决方案1】:

    您需要像应用程序一样设置预览的上下文,所以这里有一个解决方案

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            let context = (UIApplication.shared.delegate as! AppDelegate)
                  .persistentContainer.viewContext
            return ContentView()
                      .environment(\.managedObjectContext, context)
        }
    }
    

    【讨论】:

    • 感谢您快速正确的回复!抱歉,如果这是一个愚蠢的问题,我刚刚完成了 SwiftUI 课程,并且正在尝试学习 CoreData,所以我仍在尝试了解一切是如何工作的。
    猜你喜欢
    • 2020-07-26
    • 1970-01-01
    • 2020-06-06
    • 2011-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多