iOS 13.4 Приложение CoreData SwiftUI аварийно завершает работу с сообщением «EXC_BREAKPOINT (code = 1, subcode = 0x1f3751f08)» на устройстве - PullRequest
3 голосов
/ 25 марта 2020

Очень простое приложение 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)
  }
}

Снимок экрана модели:

Model

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)")
      }
    }
  }
}

1 Ответ

3 голосов
/ 25 марта 2020

Я тоже считаю, что это ошибка.

В настоящее время вы можете обойти эту проблему, снова установив переменную среды в NavigationLinks в ContentView:

NavigationLink(destination: MessageList().environment(\.managedObjectContext, moc)) {
      Text("Messages")
    }.padding()
NavigationLink(destination: PostList().environment(\.managedObjectContext, moc)) {
      Text("Posts")
    }.padding()

РЕДАКТИРОВАТЬ:

Просто заметил, что это Обходной путь имеет по крайней мере один серьезный отрицательный побочный эффект: если @FetchRequest в целевом представлении использует sortDescriptor, а само целевое представление содержит NavigationLink (например, для DetailView), то изменение атрибута, содержащегося в sortDescriptor в DetailView, приведет к вызвать выталкивание и повторный просмотр DetailView, как только новое значение атрибута приведет к новому порядку сортировки.

Чтобы продемонстрировать это:

a) добавьте новый атрибут типа Integer 16 с именем «значение» для сущности Message в модели Core Data.

b) обновите fun c addMessage () следующим образом:

func addMessage() {
    let m = Message(context: moc)
    m.title = "Message: \(Date())"
    m.value = 0
    try! moc.save()
}

c) добавьте следующую структуру в ContentView.swift

struct MessageDetailList: View {
    @ObservedObject var message: Message
    var body: some View {
        Button(action: {
            self.message.value += 1
        }) {
            Text("\(message.title ?? "?"): value = \(message.value)")
        }
    }
}

d) Обновите ForEach в struct MessageList следующим образом:

ForEach(messages, id: \.self) { message in
        NavigationLink(destination: MessageDetailList(message: message).environment(\.managedObjectContext, self.moc)) {
            Text("\(message.title ?? "?"): value = \(message.value)")
        }
    }

e) замените @FetchRequest в MessageList с:

@FetchRequest(entity: Message.entity(), sortDescriptors: [NSSortDescriptor(key: "value", ascending: false)])

Запустите код и нажмите «Сообщения». Создайте три сообщения, затем нажмите на третье. В DetailView нажмите на кнопку. Это увеличит атрибут значения этого сообщения до 1 и, таким образом, прибегнет к результатам извлечения из MessageList, что вызовет всплывающее окно и pu sh снова из подробного списка.

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...