How to use Realm with SwiftUI

家住魔仙堡 提交于 2019-12-03 10:03:46

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.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!