Documents will not read per Documentation

We appear to be missing something when it comes to a simple document read. We are using the Hotel structure presented in the documentation and one set of code works (from the documentation) and a another set of code does not (also from the docs)

In referencing the documentation here

This section of code works correctly.


let collection: Collection
do {
    let maybeCollection = try self.database.createCollection(name: "hotel")
    collection = maybeCollection
} catch {
    print(error.localizedDescription)
    return
}

let query = QueryBuilder.select(SelectResult.all()).from(DataSource.collection(collection))

do {
    let results = try query.execute()
    for row in results {
        let docsProps = row.dictionary(at: 0)!
        let docid = docsProps.string(forKey: "id")!
        let name = docsProps.string(forKey: "name")!
        let type = docsProps.string(forKey: "type")!
        print("\(docid): \(name), \(type)")

when run, this outputs the docId, name and type to console.

However the code referenced in the link crashes on this line

this_hotel.id = thisJsonObj["id"] as! String

with a “Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value”

And the complete code

do {
    let results = try query.execute()
    for row in results {

        let jsonString = row.toJSON()

        let thisJsonObj:Dictionary =
        try ( JSONSerialization.jsonObject(
            with: jsonString.data(using: .utf8)!,
            options: .allowFragments)
              as? [String: Any])!

        // Use Json Object to populate Native object
        // Use Codable class to unpack JSON data to native object
        //let this_hotel: Hotel = try JSONDecoder().decode(Hotel.self, from: jsonString.data(using: .utf8)!)

        // ALTERNATIVELY unpack in steps
        let hotelDocId = thisJsonObj["id"] as! String //crash here
        let hotelName = thisJsonObj["name"] as? String
        let hotelType = thisJsonObj["type"] as? String
        print("\(hotelDocId): \(hotelName), \(hotelType)")
    }

Note the only difference between the above code and the documentation is //let this_hotel is commented out and the docId etc are set to vars.

Also, using the exact code documentation also has errors on name, type and city lines as

“Value of optional type ‘String?’ must be unwrapped to a value of type ‘String’”

Not sure where we are going wrong. Any suggestions would be appreciated.

There is no property “id” in thisJsonObj. Unfortunately, I don’t know enough about the rest to tell you why. The Leave Feedback link on the documentation page can be used to open a ticket for the owner of the documentation.

Thanks @mreiche - this code works

so the ‘id’ property exists on the object. Perhaps this

let docsProps = row.dictionary(at: 0)!

is the key but that’s not shown in the documentation for the second section of code.

I don’t know why the documentation is showing you how to convert the result row to JSON and then parse the JSON to get values out of it. That’s just a very expensive no-op, compared to getting the values directly. Please don’t do that.

If you’re getting a crash from an unexpected nil, then your next step should be to examine the data and figure out why it doesn’t have the property you expected.

Note that using “!” is discouraged in Swift unless you can be 100% certain that the value will not be nil. You should use “?” instead and handle errors, especially when handling schemaless data where you have no guarantee that it has the ‘correct’ shape.

Yes, examples in documentation will use “!”, but only for simplicity. It’s expected that in real code you’ll add failure handling.

Can you check that the document has a property name “id”? This would be an actual property, not the document id (aka document key, aka “metaId”).

I don’t understand. It looks like it is shown.

   // ALTERNATIVELY unpack in steps
    this_hotel.id = thisJsonObj["id"] as! String

The crashing code is parsing the query row as a dictionary and expecting it to have an “id” property.

The successful code is expecting the query row to be an array whose first element is a dictionary, then gets an “id” property from the dictionary.

1 Like

Thank you @mreiche and @jens

We are very familiar with Swift development (knowing when to safely unwrap optionals !/?) and are trying to use the code presented in your own documentation.

Yes - we don’t know either but that’s what’s in the docs (see above link and below screen shot).

The section is describing the use of result.toJSON() so I can see the tie in but we are unable to make that code work due to the crash. We copy/pasted the code and used as-is, other than the small changes noted above which do not impact its operation.

What I was saying was this line of code

let docsProps = row.dictionary(at: 0)!

which is the section that works, is what jens states - the result row has an array whose first element is a dictionary, and then gets the id property from that dictionary. The code that is NOT working doesn’t appear to do that which is the basis of the question.

Is that documentation just wrong? Or are we missing something?

Here’s a screen shot of your documentation:

Assuming you’ve copied the documentation correctly, then the documentation is wrong.

When I debug things, I print out all the intermediate objects to ensure that they are what I think that they are.

@mreiche Yes! We’ve gotten into that habit over the last 45 years of coding as well. :wink:

But we also want to ensure we’re not ‘missing’ something or by assuming the docs are incorrect - or we may be misinterpreting them.

The object’s data and format are correct per my question, and properly presented in a results row - just wasn’t sure if there was something going on under the hood with .toJSON where the crashing code should work.

So going forward, is the generally most correct way to parse results?


do {
    let results = try query.execute()
    for row in results {
        let docsProps = row.dictionary(at: 0)!
        let docid = docsProps.string(forKey: "id")!
        let name = docsProps.string(forKey: "name")!

and this


let jsonString = row.toJSON() //row is a results row
var this_hotel: Hotel = try JSONDecoder().decode(Hotel.self, from: jsonString.data(using: .utf8)!)

just doesn’t work?

wasn’t sure if there was something going on under the hood with .toJSON where the crashing code should work.

That’s where printing out the result would help.

@mreiche Perhaps I misunderstand, I thought that’s what we were doing.

That’s the part that works fine. The part that is the problem is thisJsonObject. So maybe print out jsonString and thisJsonObject right after creating them. I suspect jsonString and thisJsonObject do not contain what we expect them to contain.

The code snippet in the example 28 doesn’t show the exact query that is created. If the SelectResult.all() is used, then the code snippet is wrong as the returned data will be wrapped into a dictionary for the SelectResult.all().

I have created a doc ticket to fix the code snippet.

Thank you @pasin, that provides clarity.

We appreciate everyones help - migrating a couple hundred thousand lines of code and dozens of objects from MongoDB Realm to another platform is proving to be quite a daunting task, so ensuring it’s a good fit and there are no surprises is critical.