GraphQL to Swift in a Non-Synced Realm

Every question I seem to find about converting GraphQL results to Swift Objects or using the remote access api in swift only has one answer right now which is to add the objects to a non synced realm but there’s no documentation whatsoever. So I’d love some help with this from anyone who has implemented this in their app. I’d be grateful because I’ve been trying continuously to figure it out.

For context I’m using an atlas function to search a query and return the resulting documents as objects to the user in a list. When the user taps on the ListCell it takes them to a detail view so I need the full object. I can’t use projections or individual values from the results to construct my search view.

I’ve tried to use the Object(init: value) and it always fails with the same error citing that ObjectId was not found.

I’ve tried the BSON library on Github to try and decode the BSON values but that didn’t work either.

At this point the only solution I haven’t tried is parsing through each Document and decoding everything using a switch statement and assign values individually to each field for every object. I know that will definitely work but That just seems messy and def not the most elegant solution.

If anybody has a better solution or can tell me how to add add objects from a [String : AnyBSON] search result to a realm. Please help.

Thank you for your time.

That’s a bit vague. You would only need to add the objects to a non-synced realm if you wanted to persist the objects.

If you don’t want to persist them, you can really take the data returned from the remote access query and add that to any kind of object, a pure Swift object or a Realm object.

If you want to persist the data, then well, that’s how Realm works. Instantiate the object, populate the properties and write it.

Can you clarify what the use case is?

I can convert them into any realm objects? How is that possible?

Okay let me share some schema and provide more context.

this is the basic user object. the actual schema is much more complex with a lot of fields but this is the basic idea of it

User: Object {

    @Persisted(primarykey: true) var _id: ObjectId
    @Persisted var uid : String // _id.stringValue
    @Persisted var firstName: String
    @Persisted var lastName: String
    @Persisted var created : Date
    @Persisted var age : Int
    @Persisted var gender : Gender
    @Persisted var limit: Double
 
    @Persisted var following = MutableSet<User>() 

} 

I have a search view where users can type a query and it return a fuzzy search results of top 20 possible documents to display a list view which navigates to display a profile view which is expecting an @ObservedRealmObject of type User. Now how can i go from 20 [String : AnyBSON] dictionaries to a profile view on tap. I dont need the objects to persist but i need to them to be converted into realm objects so my profile view can work with them.

Can you clarify what you are using as they are quite different:

or

If you using atlas functions, are you hitting endpoints to retrieve the data? If so, Remote Access is going to be quite a bit more streamlined.

Hello,

I’m currently using an atlas function to retrieve the data. This is the exact function I’m using.

exports = async function(query){
  
  let users = context.services.get("mongodb-atlas").db("test").collection("User");
  
  let pipeline = [
  {
    '$search': {
      'index': 'default',
      'text': {
        'query': query,
        'path': {
          'wildcard': '*'
        },
        'fuzzy': {
          "maxEdits": 2,
          "maxExpansions": 20,
        }
      }
    }
    
  },
    {
      $sort: {value: -1 },
    },
    {
      $limit : 20
    }
];
  
  return users.aggregate(pipeline);
};

Thanks for that.

The server function isn’t so important as to how you’re calling it and handling the returned data within so perhaps seeing that code would help.

Again, some code would help but let me give a verbose example - and this may be absolutely no help at all but just covering some options:

Here’s a simple User model

class User: Object {
    @Persisted var firstName: String
    @Persisted var lastName: String
    @Persisted var age : Int
}

Then some fake data; this is an example of 3 [String: AnyBSON] contained in a yourData array.

let firstName = AnyBSON(stringLiteral: "some first name")
let lastName = AnyBSON(stringLiteral: "some last name")
let age = AnyBSON(integerLiteral: 55)

let yourData = [
    "firstName": firstName,
    "lastName": lastName,
    "age": age
]

Then, once yourData is received, process it to create a user object

let user = User()

yourData.forEach { dict in
    switch dict.key {
    case "firstName":
        user.firstName = dict.value.stringValue ?? "No first name"
    case "lastName":
        user.lastName = dict.value.stringValue ?? "No last name"
    case "age":
        user.age = dict.value.asInt() ?? 0
    default:
        print("oops, key not found")
    }
}

print(user)

and the output

User {
	firstName = some first name;
	lastName = some last name;
	age = 55;
}

As mentioned, that’s verbose. You could leverage Codeable protocols to instantiate the objects directly from the returned data.

If you want to work with Realm Documents (and the returned data may be a document depending on your code)

//create a fake [String: AnyBSON] document
let myDocument = AnyBSON(dictionaryLiteral: ("firstName", firstName), ("lastName", lastName), ("age", age) )

//and then process it
switch myDocument {
case .document(let d):
    print(d)
default:
    break
}

and the output

["lastName": Optional(RealmSwift.AnyBSON.string("some last name")), "firstName": Optional(RealmSwift.AnyBSON.string("some first name")), "age": Optional(RealmSwift.AnyBSON.int64(55))]

Thank you for your time, Jay. Your answer was enough for me to devise a proper solution for my problem.

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