How to use Realm with SwiftUI

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.


@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.

Tags:

Swiftui

Realm