Очень простое приложение CoreData: весь приведенный ниже код.
- Запуск с приложением с одним представлением шаблона CoreData.
- 2 объекта со строковым атрибутом каждый: Сообщение (заголовок) и сообщение (имя)
A NavigationView, содержащее
- NavigationLink к списку сообщений
- NavigationLink к списку сообщений
У каждого связанного ListView (Message / Post) есть
- кнопка для добавления элемента в список
- кнопка для удаления всех элементов из списка
Теперь, когда вы запускаете это приложение на симуляторе (любая версия iOS 13.x), все работает, как и ожидалось из приведенного выше описания.
Но на УСТРОЙСТВЕ, работающем iOS 13.4
- Нажмите «Сообщения»
- Создание / удаление сообщений работает нормально, SwiftUi автоматически обновляется.
- Нажмите «назад»
- Нажмите «Сообщения» еще раз. Хотя создание / удаление сообщений по-прежнему работает нормально: теперь отладчик показывает предупреждение: «Контекст в среде не подключен к постоянному координатору хранилища: NSManagedObjectContext: 0x280ed72c0
- Нажмите« Сообщения »==> Приложение аварийно завершает работу с EXC_BREAKPOINT ( code = 1, subcode = 0x1f3751f08)
Вы также можете сначала запустить процесс с помощью сообщений. Затем то же самое cra sh происходит в представлении списка сообщений.
Я сильно полагаю, что это ошибка iOS 13.4, потому что подобный код нормально работал на Xcode 11.3 / iOS 13.3.
Кто-нибудь знает исправление или обходной путь для этого?
Вот ссылка на полный проект: Полный Xcode Project
ContentView:
import SwiftUI
import CoreData
struct MessageList: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: Message.entity(), sortDescriptors: [])
var messages: FetchedResults<Message>
var body: some View {
List() {
ForEach(messages, id: \.self) { message in
Text(message.title ?? "?")
}
}
.navigationBarItems(trailing:
HStack(spacing: 16) {
Button(action: deleteMessages) {
Image(systemName: "text.badge.minus")
}
Button(action: addMessage) {
Image(systemName: "plus.app")
}
}
)
}
func addMessage() {
let m = Message(context: moc)
m.title = "Message: \(Date())"
try! moc.save()
}
func deleteMessages() {
messages.forEach {
moc.delete($0)
}
}
}
struct PostList: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: Post.entity(), sortDescriptors: [])
var posts: FetchedResults<Post>
var body: some View {
List {
ForEach(0..<posts.count, id: \.self) { post in
Text(self.posts[post].name ?? "?")
}
}
.navigationBarItems(trailing:
HStack(spacing: 16) {
Button(action: deletePosts) {
Image(systemName: "text.badge.minus")
}
Button(action: addPost) {
Image(systemName: "plus.app")
}
}
)
}
func addPost() {
let p = Post(context: moc)
p.name = "Post \(UUID().uuidString)"
try! moc.save()
}
func deletePosts() {
posts.forEach {
moc.delete($0)
}
try! moc.save()
}
}
struct ContentView: View {
@Environment(\.managedObjectContext) var moc
var body: some View {
NavigationView {
VStack(alignment: .leading){
NavigationLink(destination: MessageList()) {
Text("Messages")
}.padding()
NavigationLink(destination: PostList()) {
Text("Posts")
}.padding()
Spacer()
}
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct ContentView_Previews: PreviewProvider {
static let moc = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
static var previews: some View {
ContentView()
.environment(\.managedObjectContext, moc)
}
}
Снимок экрана модели:
SceneDelegate (без изменений из шаблона, для полноты):
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = ContentView().environment(\.managedObjectContext, context)
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) {}
func sceneDidBecomeActive(_ scene: UIScene) {}
func sceneWillResignActive(_ scene: UIScene) {}
func sceneWillEnterForeground(_ scene: UIScene) {}
func sceneDidEnterBackground(_ scene: UIScene) {
(UIApplication.shared.delegate as? AppDelegate)?.saveContext()
}
}
AppDelegate (без изменений из шаблона, для полноты):
import UIKit
import CoreData
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "Coredata134")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}