I'm using Realm in my SwiftUI project to store some of my data.
Currently I store there ConversationObject and SettingObject types.
Each of objects have it's corresponding Repository
(e.g. ConversationsRepository):
final class ConversationsRepository: ConversationsRepositoryProtocol {
@Inject private var realmManager: RealmManaging
init() {}
func getConversations() -> AnyPublisher<[Conversation], Error> {
realmManager.objects(ofType: ConversationObject.self)
.map { $0.map({ $0.toModel }) }
.eraseToAnyPublisher()
}
[...]
}
which utilises generic RealmManager
class RealmManager: RealmManaging {
let realm = try? Realm()
private let realmQueue: DispatchQueue = DispatchQueue(label: "realmQueue")
private let updatePolicy: Realm.UpdatePolicy = .modified
private enum RealmError: Error {
case realmConstructionError
}
private init() {}
static let shared: RealmManaging = {
RealmManager()
}()
func objects<T: Object>(ofType: T.Type) -> AnyPublisher<[T], Error> {
Future { promise in
self.realmQueue.async {
do {
let realm = try Realm()
if realm.isInWriteTransaction || realm.isPerformingAsynchronousWriteOperations {
try realm.commitWrite()
}
let objects = realm.objects(T.self)
promise(Result.success(objects.compactMap { $0 }))
} catch {
promise(Result.failure(error))
}
}
}
.eraseToAnyPublisher()
}
[...]
}
In my project every View
struct ChatView: View {
@StateObject private var chatViewModel: ChatViewModel = ChatViewModel()
[...]
}
has it's ViewModel which manages Repository data and passes it to corresponding View
final class ChatViewModel: ObservableObject {
@Inject private var conversationsRepository: ConversationsRepositoryProtocol
@Published var currentConversation: Conversation?
@Published var conversations: [Conversation] = []
private var cancelBag: Set<AnyCancellable> = Set<AnyCancellable>()
init() {
fetchConversations()
}
func fetchConversations() {
conversationsRepository.getConversations()
.receive(on: DispatchQueue.main)
.sink { _ in
} receiveValue: { [weak self] fetchedConversations in
guard let self = self,
!fetchedConversations.isEmpty else {
self?.addNewConversation()
return
}
self.conversations = fetchedConversations
self.sortConversations()
self.currentConversation = self.conversations.last
}
.store(in: &cancelBag)
}
}
My problem is I have ChatView which displays data for ConversationObject collection, but I have SettingsView in which I can delete all ConversationObjects. Both views' viewmodels use ConversationRepository to do their stuff. But I want the data in ChatViewModel reload when I delete ConversationObject data in SettingsView
I want to create observers that I could use in various ViewModels so that they could have latest data for my Realm collections and react to changes mutating their views.
I know that I could just simply use Realm object inside my ViewModels and create Realm Notification Token there, but I want to avoid using Realm object explicitly in my ViewModels, would prefer to do indirectly by calling some repository function which later calls generic RealmManager function, unless it is not possible.
I have decided to go with Realm's built-in features that are created especially for SwiftUI. Therefore in every view which ViewModel's should have Realm's data I implemented
It is then passed to ViewModel with modifier
Later, the method
getConversationsconverts it from Realm Object to my other structure that is assigned to@Published varand this data is displayed on screenThe same technique is applied to SettingsObjects, SettingsView and SettingsViewModel respectively. By using this approach I was able to get automatically refreshing Realm data I want.