Overview
Las vistas previas de SwiftUI son una herramienta útil durante el desarrollo. Puedes trabajar con datos de Realm en las vistas previas de SwiftUI de varias maneras:
Inicializar objetos individuales para utilizarlos en vistas detalladas
Utilice condicionalmente una matriz de objetos en lugar de
@ObservedResultsCrea un reino que contenga datos para las vistas previas
La depuración de la vista previa de SwiftUI puede ser opaca, por lo que también tenemos algunos consejos para depurar problemas con reinos persistentes dentro de las vistas previas de SwiftUI.
Inicializar un objeto para una vista de detalle
En el caso más simple, puede usar las vistas previas de SwiftUI con uno o más objetos que usen propiedades de Realm que se pueden configurar directamente durante la inicialización. Podría ser útil hacer esto al previsualizar una vista de detalle. Considere el DogDetailView de DoggoDB:
struct DogDetailView: View { var dog: Dog var body: some View { VStack { Text(dog.name) .font(.title2) Text("\(dog.name) is a \(dog.breed)") AsyncImage(url: dog.profileImageUrl) { image in image.resizable() } placeholder: { ProgressView() } .aspectRatio(contentMode: .fit) .frame(width: 150, height: 150) Text("Favorite toy: \(dog.favoriteToy)") } } }
Crea una extensión para tu objeto de modelo. La ubicación de esta extensión depende de la convención de tu código base. Puedes colocarla directamente en el archivo del modelo, tener un directorio dedicado para datos de ejemplo o usar alguna otra convención en tu código base.
En esta extensión, inicialice uno o más objetos Realm con static let:
extension Dog { static let dog1 = Dog(value: ["name": "Lita", "breed": "Lab mix", "weight": 27, "favoriteToy": "Squeaky duck", "profileImageUrl": "https://www.corporaterunaways.com/images/2021/04/lita-768x768.jpeg"]) static let dog2 = Dog(value: ["name": "Maui", "breed": "English Springer Spaniel", "weight": 42, "favoriteToy": "Wubba", "profileImageUrl": "https://www.corporaterunaways.com/images/2021/04/maui_with_leaf-768x576.jpeg"]) static let dog3 = Dog(value: ["name": "Ben", "breed": "Border Collie mix", "weight": 48, "favoriteToy": "Frisbee", "profileImageUrl": "https://www.corporaterunaways.com/images/2012/03/ben-630x420.jpg"]) }
En este ejemplo,inicializamos objetos con un valor. Solo se pueden inicializar objetos con un valor cuando el modelo contiene propiedades que se pueden inicializar directamente. Si el objeto del modelo contiene propiedades que solo son mutables dentro de una transacción de escritura, como una propiedad de lista, debe... crea un reino para usar con tus vistas previas de SwiftUI.
Después de inicializar un objeto como extensión de la clase de modelo, puede usarlo en la vista previa de SwiftUI. Puede pasar el objeto directamente a la vista en la vista previa:
struct DogDetailView_Previews: PreviewProvider { static var previews: some View { NavigationView { DogDetailView(dog: Dog.dog1) } } }
Usar resultados observados condicionalmente en una vista de lista
Al usar @ObservedResults en una vista de lista, se abre implícitamente un dominio y se consulta. Para que esto funcione en una vista previa, se necesita un dominio con datos. Como alternativa, se puede usar condicionalmente una matriz estática en las vistas previas y usar solo la @ObservedResults variable al ejecutar la aplicación.
Puedes hacer esto de varias maneras, pero para que nuestro código sea más fácil de leer y entender, crearemos un EnvironmentValue que pueda detectar si la aplicación se está ejecutando en una vista previa:
import Foundation import SwiftUI public extension EnvironmentValues { var isPreview: Bool { #if DEBUG return ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" #else return false #endif } }
Luego, podemos usar esto como un valor de entorno en nuestra vista y cambiar condicionalmente qué variable usamos en función de si estamos o no en una vista previa.
Este ejemplo se basa en la extensión Perro que definimos anteriormente. Crearemos un dogArray como static let en nuestra extensión Perro e incluiremos los objetos de elemento que ya creamos:
static let dogArray = [dog1, dog2, dog3]
Luego, cuando iteramos a través de nuestra Lista, usamos la consulta estática dogArray si se ejecuta en una Vista Previa, o usamos la consulta @ObservedResults si no se ejecuta en una Vista Previa.
struct DogsView: View { (\.isPreview) var isPreview (Dog.self) var dogs var previewDogs = Dog.dogArray var body: some View { NavigationView { VStack { List { if isPreview { ForEach(previewDogs) { dog in DogRow(dog: dog) } } else { ForEach(dogs) { dog in DogRow(dog: dog) }.onDelete(perform: $dogs.remove) } } ... More View code
Esto tiene la ventaja de ser liviano y no persistir ningún dato, pero el inconveniente de hacer que el código de la Vista sea más detallado. Si prefiere un código de Vista más limpio, puede crear un realm con datos que use en las Vistas Previas.
Crear un reino con datos para vistas previas
En algunos casos, la única opción para ver los datos de un dominio en una vista previa de SwiftUI es crear un dominio que contenga los datos. Esto se puede hacer al rellenar una propiedad que solo se puede rellenar durante una transacción de escritura, en lugar de inicializarse directamente con un valor, como una lista o un conjunto mutable. También es recomendable si la vista depende de jerarquías de objetos más complejas que se pasan desde otras vistas.
Sin embargo, usar un realm directamente inyecta estado en las vistas previas de SwiftUI, lo que puede conllevar inconvenientes. Tanto si usas Realm como Core Data, las vistas previas de SwiftUI con estado pueden causar problemas como:
Ver datos inesperados o duplicados debido a la repetición de los pasos de creación del archivo de reino
Necesidad de realizar una migración dentro de la vista previa de SwiftUI cuando se realizan cambios en el modelo
Posibles problemas relacionados con el cambio de estado dentro de las vistas
Fallos inexplicables o problemas de rendimiento relacionados con problemas que no se muestran de forma visible en las vistas previas de SwiftUI
Puedes evitar o solucionar algunos de estos problemas con estos consejos:
Utilice un ámbito en memoria, cuando sea posible (demostrado en el ejemplo anterior)
Consulte los registros de diagnóstico para intentar resolver problemas con SwiftUI Preview
Puedes crear una variable estática para tu dominio en la extensión de tu modelo. Aquí es donde se realiza el trabajo para rellenar el dominio. En nuestro caso, creamos un Person y añadimos algunos Dog objetos a la dogs propiedad List. Este ejemplo se basa en el ejemplo anterior, donde inicializamos algunos objetos Dog en una extensión Dog.
Crearemos una extensión Person y crearemos un único objeto Person en esa extensión. Luego, crearemos un previewRealm agregando el Person que acabamos de crear y anexando los objetos de ejemplo Dog de la extensión Dog.
Para evitar agregar estos objetos repetidamente, añadimos una comprobación para ver si la Persona ya existe consultando objetos Persona y comprobando que el recuento sea 1. Si el dominio contiene una Persona, podemos usarla en nuestra vista previa de SwiftUI. De lo contrario, añadimos los datos.
static var previewRealm: Realm { var realm: Realm let identifier = "previewRealm" let config = Realm.Configuration(inMemoryIdentifier: identifier) do { realm = try Realm(configuration: config) // Check to see whether the in-memory realm already contains a Person. // If it does, we'll just return the existing realm. // If it doesn't, we'll add a Person append the Dogs. let realmObjects = realm.objects(Person.self) if realmObjects.count == 1 { return realm } else { try realm.write { realm.add(person) person.dogs.append(objectsIn: [Dog.dog1, Dog.dog2, Dog.dog3]) } return realm } } catch let error { fatalError("Can't bootstrap item data: \(error.localizedDescription)") } }
Para utilizarlos en SwiftUI Preview, nuestro código de ProfileView espera un perfil. Esto es una proyección del objeto Persona. En nuestra Vista previa, podemos obtener el realm, consultar el query para el Perfil y pasarlo a la vista:
struct ProfileView_Previews: PreviewProvider { static var previews: some View { let realm = Person.previewRealm let profile = realm.objects(Profile.self) ProfileView(profile: profile.first!) } }
Si no tiene una vista que espera que se pase un objeto de reino, sino que utiliza @ObservedResults para consultar un reino o trabajar con un reino existente, puede inyectar el reino en la vista como un valor de entorno:
struct SomeListView_Previews: PreviewProvider { static var previews: some View { SomeListView() .environment(\.realm, Person.previewRealm) } }
Utilice un reino en memoria
Cuando sea posible, utilice un ámbito en memoria para evitar algunos de los problemas relacionados con el estado que pueden surgir al usar una base de datos dentro de una vista previa de SwiftUI.
Utilice la propiedad de configuración inMemoryIdentifier cuando inicialice el reino.
static var previewRealm: Realm { var realm: Realm let identifier = "previewRealm" let config = Realm.Configuration(inMemoryIdentifier: identifier) do { realm = try Realm(configuration: config) // ... Add data to realm
Nota
No utilice la propiedad de configuración deleteRealmIfMigrationNeeded al inicializar un dominio para las vistas previas de SwiftUI. Debido a la forma en que Apple implementó las vistas previas de SwiftUI, usar esta propiedad para evitar problemas de migración provoca el bloqueo de las vistas previas de SwiftUI.
Eliminar vistas previas de SwiftUI
Si se encuentra con otros problemas de vista previa de SwiftUI relacionados con el estado, como una falla al cargar un reino en una vista previa debido a que se requiere migración, hay algunas cosas que puede hacer para eliminar los datos de vista previa en caché.
La solución recomendada por Apple es cerrar Xcode y usar la línea de comando para eliminar todos los datos de vista previa de SwiftUI existentes.
Cerrar Xcode.
Desde la línea de comandos, ejecute:
xcrun simctl --set previews delete all
Es posible que los datos persistan después de ejecutar este comando. Esto probablemente se deba a que Xcode conserva una referencia debido a un elemento en la vista previa y no puede eliminarla. También puede intentar estos pasos para resolver el problema:
Construir para un simulador diferente
Reinicie la computadora y vuelva a ejecutar
xcrun simctl --set previews delete allBorrar los datos almacenados en la Vista previa directamente. Estos datos se almacenan en
~/Library/Developer/Xcode/UserData/Previews.
Obtenga información detallada sobre los fallos de la vista previa de SwiftUI
Si experimenta un fallo inexplicable en la vista previa de SwiftUI al usar realm, primero intente ejecutar la aplicación en el simulador. Los mensajes de error y los registros disponibles en el simulador facilitan la detección y el diagnóstico de problemas. Si puede depurar el problema en el simulador, esta es la solución más sencilla.
Si no puede replicar un fallo de SwiftUI Preview en el simulador, puede consultar los registros de fallos de la aplicación SwiftUI Preview. Estos registros están disponibles en ~/Library/Logs/DiagnosticReports/. A veces, estos registros aparecen con un retraso, así que espere unos minutos después de un fallo si no ve el registro correspondiente inmediatamente.
Usar un reino sincronizado en las vistas previas
Si tu aplicación usa Atlas Device Sync, quizás te preguntes cómo usar un dominio sincronizado en tus vistas previas de SwiftUI. Una mejor práctica es usar objetos estáticos o un dominio local que rellenes con datos para tus vistas previas de SwiftUI.
En nuestra aplicación de ejemplo, podemos obtener una vista previa de una vista asociada con Device Sync (LoginView) sin necesidad de usar un reino en absoluto:
struct LoginView_Previews: PreviewProvider { static var previews: some View { LoginView() } }
Como solo vemos la interfaz estática, no necesitamos preocuparnos por SyncContentView, que contiene la lógica para mostrar LoginView o ir a OpenSyncedRealmView. También podemos omitir la vista previa de OpenSyncedRealmView, ya que esta solo gestiona la lógica asociada con la apertura de un reino sincronizado y su llenado para DogsView. Por lo tanto, la siguiente vista que queremos ver en una vista previa es DogsView.
Afortunadamente, con Realm, el código para trabajar con la realm no importa si usa Device Sync o no; se trabaja con la realm de la misma manera. Así que podemos utilizar el mismo realm local que creamos en el ejemplo anterior en la vista previa de SwiftUI.
struct DogsView_Previews: PreviewProvider { static var previews: some View { let realm = Person.previewRealm DogsView() .environment(\.realm, realm) } }