Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /
SwiftUI

Usar Realm con SwiftUI Previews

SwiftUI Previews are a useful tool during development. You can work with Realm data in SwiftUI Previews in a few ways:

  • Initialize individual objects to use in detail views

  • Conditionally use an array of objects in place of @ObservedResults

  • Create a realm that contains data for the previews

SwiftUI Preview debugging can be opaque, so we also have a few tips to debug issue with persisting Realms within SwiftUI Previews.

In the simplest case, you can use SwiftUI Previews with one or more objects that use Realm properties you can set directly at initialization. You might want to do this when previewing a Detail view. Consider DoggoDB's DogDetailView:

struct DogDetailView: View {
@ObservedRealmObject 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. Dónde colocas esta extensión depende de la convención en tu base de código. Puedes ponerlo directamente en el archivo del modelo, tener un directorio dedicado para datos de muestra o utilizar alguna otra convención en tu base de código.

In this extension, initialize one or more Realm objects with 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 una extensión de tu clase de modelo, puedes utilizarlo en tu Vista Previa de SwiftUI. Puedes 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)
}
}
}

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 Previsualización.

This example builds on the Dog extension we defined above. We'll create an dogArray as a static let in our Dog extension, and include the item objects we already created:

static let dogArray = [dog1, dog2, dog3]

Then, when we iterate through our List, use the static dogArray if running in a Preview, or use the @ObservedResults query if not in a Preview.

struct DogsView: View {
@Environment(\.isPreview) var isPreview
@ObservedResults(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.

In some cases, your only option to see realm data in a SwiftUI Preview is to create a realm that contains the data. You might do this when populating a property that can only be populated during a write transaction, rather than initialized directly with a value, such as a List or MutableSet. You might also want to do this if your view relies on more complex object hierarchies being passed in from other views.

However, using a realm directly does inject state into your SwiftUI Previews, which can come with drawbacks. Whether you're using Realm or Core Data, stateful SwiftUI Previews can cause issues like:

  • Ver datos inesperados o duplicados por repetir los pasos de creación de archivos de Realm

  • Needing to perform a migration within the SwiftUI Preview when you make model changes

  • Potential issues related to changing state within views

  • Bloqueos inexplicables o problemas de rendimiento relacionados con problemas que no se muestran de manera visible en SwiftUI Previews

Puedes evitar o solucionar algunos de estos problemas con estos consejos:

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

If you don't have a View that is expecting a realm object to be passed in, but instead uses @ObservedResults to query a realm or otherwise work with an existing realm, you can inject the realm into the view as an environment value:

struct SomeListView_Previews: PreviewProvider {
static var previews: some View {
SomeListView()
.environment(\.realm, Person.previewRealm)
}
}

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.

Use the inMemoryIdentifier configuration property when you initialize the realm.

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.

If you run into other SwiftUI Preview issues related to state, such as a failure to load a realm in a Preview due to migration being required, there are a few things you can do to remove cached Preview data.

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.

  1. Close Xcode.

  2. From your command line, run:

    xcrun simctl --set previews delete all

It's possible that data may persist after running this command. This is likely due to Xcode retaining a reference due to something in the Preview and being unable to delete it. You can also try these steps to resolve issues:

  • Build for a different simulator

  • Restart the computer and re-run xcrun simctl --set previews delete all

  • Borrar los datos almacenados en la Vista previa directamente. Estos datos se almacenan en ~/Library/Developer/Xcode/UserData/Previews.

Si tienes un bloqueo inexplicable de Preview de SwiftUI al usar realm, primero intenta ejecutar la aplicación en el simulador. Los mensajes de error y los registros disponibles para el simulador facilitan la identificación y el diagnóstico de problemas. Si puedes depurar el problema en el simulador, esta es la ruta más fácil.

If you cannot replicate a SwiftUI Preview crash in the simulator, you can view crash logs for the SwiftUI Preview app. These logs are available in ~/Library/Logs/DiagnosticReports/. These logs sometimes appear after a delay, so wait a few minutes after a crash if you don't see the relevant log immediately.

If your app uses Atlas Device Sync, you may wonder how to use a synced realm in your SwiftUI Previews. A better practice is to use static objects or a local realm that you populate with data for your SwiftUI Previews.

En nuestra aplicación de ejemplo, podemos obtener una vista previa de una vista asociada con Device Sync - la LoginView - sin necesitar usar un realm en absoluto:

struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView()
}
}

Since we're only viewing the static UI, we don't need to worry about the SyncContentView that contains the logic of whether to show the LoginView or go to the OpenSyncedRealmView. We can also skip previewing the OpenSyncedRealmView, because that just handles logic associated with opening a synced realm and populating it for the DogsView. So the next view we want to see in a Preview is the 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)
}
}

Volver

Sync Data in the Background

En esta página