0

All the solutions I have found online are for local Realms, not synced Realms (I am using Query based sync). How to do it right for a synced Realm?

I have a Shop object and an Item object. Shop has many items. User can browse the items and should see which shop that item belongs to. Pretty simple and straight forward scenario.

In the Shop class I have:

let items = List<Item>()

and in the Item class I have

let shops = LinkingObjects(fromType: Shop.self, property: "items")
var shop: Shop? { return shops.first }

The Realm query is like this:

private var realm             : Realm!
private var subscriptionToken : NotificationToken?
private var syncSubscription  : SyncSubscription!

...
...
...

let items = realm.objects(Item.self)
syncSubscriptionItem = items.subscribe()
subscriptionTokenItem = syncSubscriptionItem.observe(\.state, options: .initial) { state in
    if state == .complete {
        let shopName = items[0].shop?.name // Shop is always nil
    }
}

I can see shop only if the shop's owner has logged into the app, which means Realm has synced shop information to local Realm. But for users on other devices, shops never get synced. But how to sync shops for all other users via this type of back linking?

Screenshot is attached to clarify what I mean:

enter image description here

zeeshan
  • 4,913
  • 1
  • 49
  • 58
  • It looks like you're using Query-Based Sync. You should convert to Full as [Query-Based Sync is not recommended](https://docs.realm.io/sync/using-synced-realms/choosing-your-sync-type#query-based-sync) and appears to be depreciated. Based on the chatter it won't be supported at some point in the near future. – Jay May 08 '20 at 14:54
  • @jay yes, I am using Query based sync. As I understand, Full sync syncs entire database onto local device. Won't it make local Realm unnecessarily too big, with lots of unnecessary information. It'll become space management issue on the device. – zeeshan May 08 '20 at 18:51
  • The size will depend on the use case. Unfortunately, you won't have a choice. As that capability goes away you'll have to move to full sync. MongoDB Realm may provide other options but that is still aways out and the roadmap for sync'ing is totally unclear. – Jay May 08 '20 at 19:17
  • @jay in other words, inverse linking is not usable for query-based syncs, right? I have an alternative way to achieve what I am trying to do, remaining with-in query-based sync, but just wanted to be sure. At this time, changing my code base for full-sync is not possible. I have gone through Realm documentation again and see how they propose to use full-sync while managing devices' storage, but it will require some major new code and Realm schemas. – zeeshan May 08 '20 at 19:46
  • I would say that's not accurate; From the docs - *query on a parent object and all child objects will be automatically pulled in* – Jay May 09 '20 at 14:53

1 Answers1

0

I took a minute and wrote some code based on yours. The code in the question was not complete so I added all of the other elements to the code to paint a more complete picture. Theres really no error checking here so make sure you add that. This is working for me and outputs all of the items associated shops

var items: Results<Item>? = nil
var notificationToken: NotificationToken? = nil
var subscriptionToken: NotificationToken? = nil
var subscription: SyncSubscription<Item>!
var realm: Realm!

func setupItems() {
    let config = SyncUser.current?.configuration()
    self.realm = try! Realm(configuration: config!)

    self.items = self.realm.objects(Item.self)

    self.subscription = self.items!.subscribe(named: "my-items")

    self.subscriptionToken = subscription.observe(\.state, options: .initial) { state in
        if state == .complete {
            print("subscription: queried data has been synced")
        } else {
            print("Subscription State: \(state)")
        }
    }

    self.notificationToken = self.items!.observe { changes in
        switch changes {
        case .initial:
            // Results are now populated and can be accessed without blocking the UI
            print("notification: initial results are populated")
            for item in self.items! {
                if let shop = item.shop {
                    print(shop.name)
                } else {
                    print("\(item.itemName) has no shop")
                }
            }
        case .update(_, let deletions, let insertions, let modifications):
            // Query results have changed
            print("notification: results, inserted, deleteed or modified")
        case .error(let error):
            // An error occurred while opening the Realm file on the background worker thread
            fatalError("\(error)")
        }
    }
}
Jay
  • 34,438
  • 18
  • 52
  • 81
  • Thanks for taking time to write this code. This code will work fine on a device on which you have created both the shop and the items. But now try it on a different device which fetches these items. On this other device there will be no shops. I have used your code and showing results in attached screenshot in my question. My original code was shortened version relevant to just the problem in question. – zeeshan May 09 '20 at 18:17
  • @zeeshan I have almost this exact code running on three different Macs (at two separate locations) and it is working and syncing between them. There must be some other difference between your code and this - you mention other users... do you have permissions set up correctly? – Jay May 09 '20 at 20:32
  • I'll write a small separate project to test this out and get back to you. There must be something off in my code which might be simple but I haven't figured it out yet. From users I meant users on the devices, on Realm I have only one admin user. – zeeshan May 11 '20 at 09:21