Exception caught in map block of view - Invalid type in JSON write (_SwiftValue)

I’m trying to use a CBLView for indexing/filtering the documents on two channels, having the key “waitMinutes” and emitting the document id and the document itself:

func queryFacilityStatuses () -> CBLQuery{
    let view : CBLView = database.viewNamed("facilityStatuses")
    
    if (view.mapBlock == nil) {
        view.setMapBlock({ (document, emit) in
            if (document.index(forKey: "waitMinutes") != nil) {
                emit(document["id"], document) //Breakpoint here
            }
            }, version: "0.0.6")
    }
    
    return view.createQuery()
}

When the emit function is called for any document with this structure, an exception is presented. I put a breakpoint on the emit line and printed the document:

po print(document)

["singleRider": 0, "status": Operating, "_id": dlr.facilitystatus.1_0.Attraction.353451, "id": 353451;entityType=Attraction, "channels": <__NSArrayM 0x600000c53aa0>(
dlr.facilitystatus
)
, "fastPassEndTime": , "waitMinutes": 50, "_rev": 1-47ddbd3d993086de0be5947b426ed986, "fastPassAvailable": 1, "fastPassStartTime": FASTPASS is Not Available, "_local_seq": 1]

This is the document on the Sync Gateway:

{
  "channels": [
    "dlr.facilitystatus"
  ],
  "fastPassAvailable": true,
  "fastPassEndTime": "",
  "fastPassStartTime": "FASTPASS is Not Available",
  "id": "353451;entityType=Attraction",
  "singleRider": false,
  "status": "Operating",
  "waitMinutes": 50,
  "_rev": "1-47ddbd3d993086de0be5947b426ed986",
  "_id": "dlr.facilitystatus.1_0.Attraction.353451"
}

And this is the exception trace:

08:23:27.725‖ WARNING: Exception caught in map block of view facilityStatuses, on doc {
    "_id" = "dlr.facilitystatus.1_0.Attraction.353451";
    "_local_seq" = 1;
    "_rev" = "1-47ddbd3d993086de0be5947b426ed986";
    channels =     (
        "dlr.facilitystatus"
    );
    fastPassAvailable = 1;
    fastPassEndTime = "";
    fastPassStartTime = "FASTPASS is Not Available";
    id = "353451;entityType=Attraction";
    singleRider = 0;
    status = Operating;
    waitMinutes = 50;
}:
	Invalid type in JSON write (_SwiftValue)
	3   Foundation                          0x0000000111263284 _writeJSONValue + 668
	4   Foundation                          0x00000001112636d9 ___writeJSONArray_block_invoke + 132
	5   CoreFoundation                      0x0000000118e1ce2a -[__NSSingleObjectArrayI enumerateObjectsWithOptions:usingBlock:] + 58
	6   Foundation                          0x00000001112633df _writeJSONArray + 330
	7   Foundation                          0x0000000111263204 _writeJSONValue + 540
	8   Foundation                          0x0000000111262f94 -[_NSJSONWriter dataWithRootObject:options:error:] + 124
	9   Foundation                          0x0000000111262e74 +[NSJSONSerialization dataWithJSONObject:options:error:] + 333
	10  DLR                                 0x000000010e50300e NRMA__ptrIntPtrParamHandler + 190
	11  DLR                                 0x000000010e50211c NRMA__blk_ptrIntPtrParamHandler + 60
	12  WDPRFinderCore                      0x00000001159fc805 +[CBLJSON dataWithJSONObject:options:error:] + 320
	13  WDPRFinderCore                      0x0000000115a29518 -[CBL_SQLiteViewStorage _emitKey:value:valueIsDoc:forSequence:] + 629
	14  WDPRFinderCore                      0x0000000115a29033 __39-[CBL_SQLiteViewStorage updateIndexes:]_block_invoke.161 + 97
	15  WDPRFinderCore                      0x0000000115951bfb _TTRXFdCb_dPs9AnyObject_dGSqPS_____XFo_iP_iGSqP____ + 219
	16  WDPRFinderCore                      0x0000000115951881 _TFFC14WDPRFinderCore25FacilityStatusServiceImpl21queryFacilityStatusesFT_CSo8CBLQueryU_FTGVs10DictionarySSP__FTP_GSqP___T__T_ + 737
	17  WDPRFinderCore                      0x000000011595199a _TTRXFo_oGVs10DictionarySSP__oXFo_iP_iGSqP______XFdCb_dCSo12NSDictionarydXFdCb_dPs9AnyObject_dGSqPS1_______ + 170
	... {at MYReportException:43}

Additional details:

  • The same configuration and documents are being used on an Android App and is working fine.
  • When I use createAllDocumentsQuery() instead of the query from a view, the documents are retrieved correctly.
  • I’m using Couchbase Lite V1.3.1 and building with Xcode 8.0 and Swift 3

Any help will be appreciated!

Interesting — this looks like a bug in NSJSONSerialization, or perhaps in the Swift/Obj-C bridging system. Somehow the string in your channels array isn’t being bridged to an NSString, and this confuses the serializer because it doesn’t know how to write the raw _SwiftValue object.

I’ll ask about this on the swift-users mailing list.

However, the workaround is simple — don’t pass document as the value to the emit function. (It’s rarely a good idea to do this; it bloats the size of the index.) If there is a specific value that you want to use when you query this view, emit that; otherwise just emit nil.

Just posted to swift-users mailing list. What platform is this running on, and what OS version?

Response from swift-users:

It does appear to have been fixed in Xcode 8.1 / Swift 3.0.1. > rdar://28365419: JSONSerialization crashes when Optional is contained in argument to data(withJSONObject:options)

Hi @jens

Thanks for your replies.

I’ve found that unwrapping the optional value that I want to emit, it works, so that’s the approach we’re going to use right now until we can migrate to Xcode 8.1.

The emit line then will be:

if let _ = document[“waitMinutes”] as? Int,
let id = document[“id”] as? String {
emit(id, nil)
}

And in the same order of your replies:

  • I tested using just emit(document["id"], nil) and the error was still happening.
  • I was testing on iOS 9/10, compiling with Xcode 7.3.1/Swift 2.3 and Xcode 8.0/Swift 3.0
  • I have not tested with Xcode 8.1 / Swift 3.0.1, so if I got the idea, using Xcode 8.1 will work without the need to unwrap the values?

Thanks!