Reputation: 2711
I have been trying to figure out how to use Realm with SwiftUI. The problem is that SwiftUI and Realm both have a List type. When you import SwiftUI into your Realm model to make the class a BindableObject and try to create a Realm List property there is an error.
Is it possible to use an instance of the Realm object model and make it a BindableObject in SwiftUI?
Upvotes: 8
Views: 8122
Reputation: 239
@JustinMiller I created a ChannelsData class that I use to listen for changes in my chat channels from a Realm collection. I then update the UI by making the ChannelsData an @EnvironmentObject in my view. Here is what works for me in Xcode 11 GM Seed:
final class ChannelsData: ObservableObject {
@Published var channels: [Channel]
private var channelsToken: NotificationToken?
// Grab channels from Realm, and then activate a Realm token to listen for changes.
init() {
let realm = try! Realm()
channels = Array(realm.objects(Channel.self)) // Convert Realm results object to Array
activateChannelsToken()
}
private func activateChannelsToken() {
let realm = try! Realm()
let channels = realm.objects(Channel.self)
channelsToken = channels.observe { _ in
// When there is a change, replace the old channels array with a new one.
self.channels = Array(channels)
}
}
deinit {
channelsToken?.invalidate()
}
And then I use an @EnvironmentObject to grab the channels for my view:
struct ChannelsContainerView: View {
@EnvironmentObject var channelsData: ChannelsData
var body: some View {
List(channelsData.channels.indexed(), id: \.1.id) { index, _ in
NavigationLink(destination: ChatView()) {
ChannelRow(channel: self.$channelsData.channels[index])
}
}
}
}
Don't worry about the indexed() function in the List. But if you're curious, it comes from Majid's clever approach to creating flexible SwiftUI data storage classes here: https://mecid.github.io/2019/09/04/modeling-app-state-using-store-objects-in-swiftui/
And if you're coming into the view from another view, be sure to add .environmentObject(ChannelsData()) to your view link (and also in your Previews) or it won't work.
Upvotes: 8
Reputation: 4096
Sure, it's very simple, use the module identifier as prefix like this :
let members = RealmSwift.List<Member>()
Now to the second part of your question. It's easy to encapsulate a Realm object (or list, or resultset) in an BindableObject
:
final class DBData: BindableObject {
let didChange = PassthroughSubject<DBData, Never>()
private var notificationTokens: [NotificationToken] = []
var posts = Post.all
init() {
// Observe changes in the underlying model
self.notificationTokens.append(posts.observe { _ in
self.didChange.send(self)
})
self.notificationTokens.append(Message.all.observe { _ in
self.didChange.send(self)
})
}
}
If you "link" a DBData
instance to a SwiftUI View
by either using @ObjectBinding
or @EnvironmentObject
the UI will be refreshed and the new value for posts
(in our example here) will be available each time the underlying realm changes.
Upvotes: 17