Writing to Realm with Flexible Sync

I’m attempting my first pass at an app using Flexible Sync.

The current error I’m getting is when writing to the Realm, this shows up on the server:

For search purposes: Client attempted a write that is outside of permissions or query filters; it has been reverted (ProtocolErrorCode = 231) cannot write to table before opening a subscription on it

Everything I’m doing thus far has been from examples, but with my own objects.

The Idea Object looks like this:

final class Idea: Object, ObjectKeyIdentifiable {
  @Persisted(primaryKey: true) var _id: String = UUID().uuidString
  @Persisted var ownerID: String = "" // user._id
  @Persisted var title: String = ""
  @Persisted var created: Date = Date()
  @Persisted var body: String = ""
}

And my sync permissions look like this:

{
  "rules": {},
  "defaultRoles": [
    {
      "name": "owner-write",
      "applyWhen": {},
      "read": true,
      "write": {
        "ownerID": "%%user.id"
      }
    }
  ]
}

And I’m creating a flexibleSyncConfiguration with initialSubscription like this:

    if let user = app.currentUser {
      let config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in
        if subs.first(named: "user_ideas") != nil {
          return
        } else {
          subs.append(QuerySubscription<Idea>(name: "user_ideas") {
            $0.ownerID == user.id
          })
        }
      })
      ContentView()
        .environment(\.realmConfiguration, config)
    }

Here is my simplified code in the ContentView(). Add some text to a state variable, show a button, tap the button to add, add the idea object and reset the state variable like this:

struct ContentView: View {
  @ObservedResults(Idea.self) var ideas
  @State private var newIdea = ""
  
  var body: some View {
    VStack(alignment: .leading) {
      TextEditor(text: $newIdea)
      if newIdea != "" {
        Image(systemName: "plus.circle.fill")
          .onTapGesture {
            addIdea()
          }
       }
    }
  }
  
  private func addIdea() {
    let idea = Idea()
    idea.title = newIdea
    idea.ownerID = app.currentUser!.id
    $ideas.append(idea)
    newIdea = ""
  }
}

I assumed this would be an extremely basic implementation, but the error doesn’t make any sense. There is a subscription (I did that while creating the flexible sync configuration) and that is passed in as the environment.

I looked at @Andrew_Morgan’s article about RChat, and although there are lots of spots where subscriptions are added (realm.subscriptions.write), there are no other mentions of adding data, so I assumed that we do this as we always have.

Am I missing something here?

Also having this issue. Wish I could help

Turns out the issue I had was dev mode was still on

Wow! You’re right, that fixed it. So weird that it doesn’t say anywhere in the “Development Mode” warnings / prompts / etc anything about not being able to make writes while Development Mode is on.

With Partition Sync I was able to leave development mode on while developing. It seems like I will have to just toggle Development Mode on when wanting to update the models and then toggle it off to test usage.

So weird. If anyone at Mongo could explain why this is so different (and undocumented) for Flexible, that would be great! Thanks.

1 Like

This is very weird, we do all of our tests with development mode activated and this is working. do you get any error when the view opens the realm, can you check if the subscription exist before adding the object?, It may give us a clue why this is happening.

Hi, also looking into this on the server-side and not seeing any clear reason why this would be happening but working on reproducing it. Do you mind sending a link to your application in the cloud-ui?

Dev mode seemed to have fix my issue at first but it returned. The solution was something also quite simple but a pain to figure out. I was subscribing to a table but the query didn’t give me any warnings or errors against checking a relationship collection.

For example, if I’m subscribing for projects and need to check if the members contains the current user by id or if the owner is the current user owner._id == user.id it silently failed. Replacing it with ownerId == user.id fixed it. This isn’t an ideal solution since it could make the members and membersIds out of sync. Hopefully this gets resolved or at least better documented. Spent a few hours on this issue

There is no error on the device. The button writes the Idea object to the realm. The Idea shows in a list utilizing the @ObservedResults and then quickly disappears. That’s why I checked the server logs and saw this error, that the server is reverting the change.

As far as checking if the subscription exists before adding the object, as noted above, I only have one subscription "user_ideas", and it is initialized in the flexibleSyncConfiiguration. If it doesn’t exist before adding the object, then I don’t understand what initialSubscriptions is supposed to do.

Yeah, this one I did know from this part of the documentation:

Flexible Sync only supports top-level primitive fields with a scalar type as queryable fields

It has to be top level. It’s very much the least Realmish thing about Flexible sync that I’ve found. Instead of just dealing with objects all the way down, we are now mixing MongoDB patterns that aren’t abstracted away. When linking an object, the mongodb document is just holding the _id of that object. We have to do that now in the top level with Flexible sync.

As far as it being out of sync though, that shouldn’t happen because the _id cannot change. SO it should always point to the same object if you’re pointing to immutable fields like _id.

Hope that makes sense.

The issue I have with the pattern is I was never alerted that it was wrong so the subscription succeeded.

Also for stuff being out of sync, I mean as I would need to have 2 variables membersIds and members (one for querying and one for actually getting the user objects). I could just use membersIds and query the individual members if I need but obviously thats not ideal when locally I could have just used the relationships before. I hope we get non-top level primitive querying soon

I agree, that’s a major headache that I haven’t considered.

Two top level fields:

  1. Queryable scalars
  2. Same objects that you’re also needing in 1.

That’s definitely a mess I hadn’t thought of :man_facepalming:t2:

2 Likes

With these limitations in mind, how would I accomplish something like this? Since I can’t access the relationships I don’t know any solution that would allow me to access the members of a project

subscriptions.append(QuerySubscription<SKAUserObject>(name: subscription, query: {
   $0.projects._id == id
 }))

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.