Help on opening multiple Synced Realms (SwiftUI)

Hi,

My app on iOS is relying on private and public data backed by partition-based Realm Sync. I understood in that case that two partition keys are necessary, meaning I need to open two different realms at the app launch.
Until now, I have been working on private data operations and it is working quite well. I have been basically following the structure on this page: https://www.mongodb.com/docs/realm/sdk/swift/swiftui-tutorial/#complete-code

In addition, there should be some level of interaction between the two realms. Users can create new entries in the private/public space (LibraryView) and in the other direction users can get public data copied to their private space.

struct SyncContentView: View {
	// Observe the Realm app object in order to react to login state changes.
	@EnvironmentObject var app: RealmSwift.App
	
	var body: some View {
		if app.currentUser != nil {
			OpenSyncedRealmView()
				.environment(\.partitionValue, app.currentUser?.id)
		} else {
			AuthenticationView()
		}
	}
}
// This view opens a synced realm.
struct OpenSyncedRealmView: View {
		
	@AutoOpen(appId: Constants.APP_ID, partitionValue: "", timeout: 4000) var autoOpen
	
	var body: some View {
		switch autoOpen {
			
		case .connecting:
			ProgressView()
		
		case .waitingForUser:
			ProgressView("Waiting for user to log in...")
			
		case .open(let userPartitionedRealm):
			TabView {
				LibraryView(userGroup: {  // PRIVATE data
// User should be able to write in the public realm here too...
					// ... getting group from private user-partitioned realm...
				}())
				.environment(\.realm, userPartitionedRealm)
				.tabItem {
					Label("Library", systemImage: "books.vertical")
				}
				
				BrowseView()  
// Should display PUBLIC data. 
// Users can import data from PUBLIC to their own space (PUBLIC copied to PRIVATE)
// Where to open the public realm?
					.tabItem {
						Label("Browse", systemImage: "rectangle.stack")
					}
				
				SettingsView()
					.tabItem {
						Label("Settings", systemImage: "gear")
					}
			}
			
		case .progress(let progress):
			ProgressView(progress)
			
		case .error(let error):
			ErrorView(error: error)
		}
	}
}

I don’t see exactly how I am supposed to open a second @AsyncOpen Realm and access it from both views. Ideally, I assume both realms should be accessible by LibraryView and BrowseView…

Any recommendation on the way to achieve this?

There are a couple of approaches you could consider.

With Realm’s SwiftUI property wrappers, AutoOpen/autoOpen is essentially a variable with some associated state. There’s no reason you couldn’t open both:

struct OpenSyncedRealmView: View {
     @AutoOpen(appId: Constants.APP_ID, partitionValue: "", timeout: 4000) var publicData
     @AutoOpen(appId: Constants.APP_ID, partitionValue: "", timeout: 4000) var privateData
}

You’d need to change the view body as you wouldn’t just be reacting to the AutoOpenState/AsyncOpenState of one Realm. When both have opened, you could then pass them as environment objects to the views that need them.

Another option is to ignore the SwiftUI property wrappers entirely and instead open them with the standard Swift SDK syntax:

let publicRealm = try await Realm(configuration: publicConfiguration)
let privateRealm = try await Realm(configuration: privateConfiguration)

Then, once you have them, you can pass them to the views that need them. You may find this easier to work with, as I think the SwiftUI AsyncOpen/AutoOpen property wrappers are optimized for cases where a View needs one realm.

1 Like

@Dachary_Carey
Thank you for your reply! I managed to make it work after some refactoring (without the property wrappers)
If I may, another follow-up question: is there an easier way to pass multiple Realms to child views than cascading by parameters? I was using @Environment(\.realm) before, but this does not seem to be compatible with multiple realms, unfortunately.

Sonisan, if you liked the environment variable approach, and would like to continue using it but with more than one realm… the way to handle this is thru the swift language itself.

A Swift Environment object is exposed by its type, which is what you are bumping up against when you have more than one realm object but both are of type Realm. A way to work with swift’s implementation of environment variables and expose multiple objects of the same type thru environment is to wrap each instance object into a wrapping class.

Psuedo code:
class privateRealm {
var realm: Realm // probably have some propertywrapper on this
}

class publicRealm {
var realm: Realm // probably have some propertywrapper on this
}

then you can pass them to a view:
YourView.environment(…, publicRealm).environment(…, privateRealm)

In your views:
struct YourView: View {
@EnvironmentObject var publicWrap: publicRealm
@EnvironmentObject var privateWrap: privateRealm

… now you can reference with privateWrap.realm or publicWrap.realm

}

Hope this helps

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.