20
votes

Tableau de prévisualisation et données de base SwiftUI

La toile de prévisualisation plante mais dans le simulateur, tout fonctionne correctement. Je suppose qu'il est lié à @ObservedObject et @Fetchrequest ...

Solution essayée pour ici Aperçu de ContentView avec CoreData

ne marche pas

 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)

    }
}

Devrait générer un aperçu


6 commentaires

Quelle est l'erreur?


C'est la partie amusante. Le canevas d'aperçu ne donne jamais de détails sur l'erreur ... juste la ligne sur laquelle il s'est écrasé


Assez vrai. Vous pouvez remplacer ObservedObject par EnvironmentObject. Est-ce que ça plante si vous supprimez la @FetchRequest et son interface utilisateur associée? Sinon, vous pouvez essayer le nouveau modèle SwiftUI des détails principaux dans Xcode et l'ajouter à un aperçu et comparer les différences.


Des nouvelles ici? J'ai aussi le problème. J'utilise une base de données Realm (www.realm.io) et le crash du canevas lorsque j'introduis un objet d'environnement qui utilise cette base de données.


Je ne peux pas le faire fonctionner. Séparez parfaitement la vue de génération de code de prévisualisation. Tout le code complet sans erreur aussi. Mais l'aperçu du canevas échoue tout simplement sans fournir de précision sur une erreur. Pour l'instant, j'ai d'autre choix que de le commenter et de reconstruire le projet à chaque fois que je veux voir les progrès.


Une chose importante (à partir de Xcode 12) - n'oubliez pas d'ajouter votre *.xcdatamodeld aux *.xcdatamodeld de Development Assets .


6 Réponses :


1
votes

Une option consiste à NE PAS utiliser les données de base dans les aperçus. C'est assez utile pour voir l'interface utilisateur de ce que je construis, mais je devrai toujours utiliser Simulator pour tester la fonctionnalité.

#if !DEBUG
// Core Data related code e.g. @FetchRequest
#endif

Ce qui a été suggéré dans Aperçu de ContentView avec CoreData a fonctionné pour moi, Xcode Version 11.0 (11A419c) Mac OS 10.15 Beta (19A558d). Mes journaux de crash ont montré une erreur d'index,

*** Arrêt de l'application en raison d'une exception non interceptée 'NSRangeException', raison: '*** - [__ NSArray0 objectAtIndex:]: index 0 au-delà des limites pour NSArray vide'

car il n'y avait pas de données là-bas, j'ai donc dû gérer ce cas unique de "prévisualisation" et cela a fait fonctionner les choses.


1 commentaires

Cela fait longtemps, qu'avez-vous fini par faire pour les avant-premières?



1
votes

Donc, si vous mettez du code dans un gestionnaire onAppear dans l'aperçu, il s'exécutera au démarrage. Il met même à jour en direct au fur et à mesure que vous tapez!

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  static var persistentContainer: NSPersistentContainer {
    return (UIApplication.shared.delegate as! AppDelegate).persistentContainer
  }

  static var viewContext: NSManagedObjectContext {
    return persistentContainer.viewContext
  }

Notez que j'ai enveloppé mon viewContext dans une méthode statique sur AppDelegate pour rendre l'accès un tout petit peu moins verbeux et plus facile à retenir:

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


0 commentaires

34
votes

Je ne sais pas si votre ligne d'essai fonctionnera s'il n'y a pas de données.

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

Pour que le mien fonctionne, j'ai créé un élément de test à utiliser. Comme ça:

let documentTemplate = try! managedObjectContext.fetch(request).first as! Templates

J'ai également remarqué que j'avais besoin du code .environment (.managedObjectContext, context) dans un tabView antérieur qui hébergeait les vues CoreData ou l'aperçu échouerait.


3 commentaires

La question ci-dessus a été posée et répondue de différentes manières. C'est la seule réponse qui fonctionne! L'essence du problème est que l'aperçu semble commencer avec son propre magasin persistant (vide!), Vous devez donc en quelque sorte remplir ce magasin avec suffisamment d'objets pour que toutes vos aperçus fonctionnent. J'ai créé une fonction de classe qui remplit la base de données avec des exemples d'objets si la base de données est vide. Pour chaque entité de mon modèle, j'ai également créé des fonctions de classe qui renverront l'un de ces exemples d'objets à passer en tant que paramètre selon les besoins de la vue spécifique prévisualisée.


"Je ne sais pas si votre ligne d'essai fonctionnera s'il n'y a pas de données." est correct. J'ai testé cela en récupérant des enregistrements pour une entité, ce qui fonctionne lorsque l'entité contient un ou plusieurs enregistrements existants. Cependant, lorsque je supprime les données "test" pour cette entité (ou plus facilement les fichiers SQLite), aucune donnée ne s'affiche (peut-être évidemment), à moins que j'adopte la solution dans cette réponse et que je crée un enregistrement d'entité à afficher dans l'aperçu


FWIW: cet article explique comment déboguer le code d'aperçu en direct (points d'arrêt, etc.). developer.apple.com/news/?id=8vkqn3ih



12
votes

Cette réponse semble fonctionner dans mon projet récent en remplaçant la structure ContentView_Previews par défaut, bien que d'autres se demandent si elle extrait des données persistantes. Le mérite revient à @ShadowDES - dans le projet de modèle Master / Detail dans Xcode Beta 7

Je suis capable de CRUD n'importe quoi en utilisant Canvas (XCode Version 11.3 (11C29)) et il semble fonctionner parfaitement.

    #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

Ajouter des valeurs, marquer la priorité

Faites glisser pour supprimer

Supprimé!


1 commentaires

il est incroyable de voir comment Apple propose un module de prévisualisation incapable de prévisualiser. Peu importe quel est le problème. Un aperçu doit prévisualiser. Xcode est zéro étoile.



1
votes

Fonctionne pour l'application SwiftUI 2 à l'aide du modèle d' App

J'ai également eu le crash des aperçus et aucune des autres solutions ne convenait ou ne fonctionnait pour moi.

Ce que j'ai fait était plutôt que ce qui suit:

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

Je l'ai réparé avec:

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 est:

struct ContentView_Previews: PreviewProvider {
  
    static var previews: some View {
        return ContentView()
            .environment(
                \.managedObjectContext,
                CoreDataManager.context
            )
    }
}

Je ne sais pas exactement pourquoi cela a aidé, mais maintenant cela fonctionne parfaitement. De plus, vous pouvez ajouter des exemples de données à ce context où j'ai marqué avec un commentaire.


0 commentaires

1
votes

C'est ma solution.

Je ne veux pas utiliser CoreData en vue. Je veux le style MVVM. Vous devez donc simuler les données de base pour les afficher dans la vue Canvas.

Ceci est un exemple :

// 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)


0 commentaires