Getting attachments.count does not work for Swift

I am using this with Swift.
Couchbaselite 2.1,
Server 6.0
Sync 2.1

I am saving many blobs to a single documents let’s say 3. When replicated _attachments key is created in the meta of the document. Below is the code that I’m trying to use to retrieve a count of 3. The blob for key works for retrieving the image, but trying to count this returns nil. It never works.

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let doc = _dataManager?.database?.document(withID: _docID)?.toMutable()
let attachments = doc!.dictionary(forKey: “_attachments”)

    print("attachmentsCount::\(String(describing: attachments?.count))")
    
    pageControl.numberOfPages = attachments?.count ?? 1
    
    return  attachments?.count ?? 1
}

This is not the proper way to retrieve attachments. I don’t think there is any way to retrieve the number of properties that are blobs, so instead why not put them into an array in the JSON and then you can simply get the number of elements in the array.

You may have seen the _attachments property on the server side. It’s created for compatibility reasons by the replicator, but it doesn’t show up in Couchbase Lite. Instead you should look for the blogs in the same properties where you created them.

@jens thank you for the reply. If I have multiple blobs, how to I get a count without individually calling each keys. This is common pattern for tableview and collection in ios development to have to item/row count of the objects in some type of data structure as exampled previously.

In 2.x blobs are no different than strings or ints, so ask yourself the question “how can I get a count of my string keys without individually calling each key?” The same answer will apply to blobs.

@borrrden Well int.count and strings.count all returns the characters in swift. So when I’m access a specific doc from database.document(docId), let’s call it var doc. Doc.blob(forkeys) return a single blob, and calling .content.count from this is what wa provided by couchbase lite and returns nil. So unless I retrieve doc and know all of the blob key names, which will never be the case I want for the application since I’m generating with an incrementer to add blob depending on the client side choice of how many image to attach, I would like to get the count for the blobs attached to each of these document sent. Unless I’m not suppose to do this and store it on a separate s3 bucket, and just a reference key

Just iterate the document recursively counting the number of blobs you find. I’m sorry if I’m misunderstanding you, but this seems like a pretty basic CS problem, searching a tree for matching values.

I’m basically trying to find a key for me to even access all blob. Right now I can only access 1 blob at a time if I have the key name of the individual image. Unless I hold this reference of all the images for each user ahead of time and store that, then I can parse. So I’m confused of your approach to recursely search something when it’s data structure is not even accessible

There is no key to access all blobs. There are only keys and values on the document itself. That is what you should be searching. The document itself is just a key/value map (aka dictionary).

That’s crazy, you should just put these blobs in array log objects instead of key to objects and count would be easier. What if these attachments had different name, what key would I use to recursively search for it? _attachentments is not a key I can use, neither is blob. Only your built in from couchbaselite of blob.forkey(keyname) is available. I would have to query out meta info, you don’t have a key that’s usable.

So you want the user to full parse documents every time without offering any convenience method, when you’re offering all these other convenience methods, it seems a missing a crucial part for ease of use. I would suggest to have such functionality for ease of development for people who is using this over other service. It has been easy and hard to use due to great concept but incompleteness.

What comparable convenience methods are you referring to? Why should we treat blobs specially? Let’s say there is a document like this:

{
      "blob1": <blob>,
      "string1": "foo",
      "int1": 42,
      "blob2": <blob>,
      "int2": 100
}

Take your problem and replace “blob” with “int”. There is no way to find out how many keys in the document contain ints (i.e. int1 and int2), so why should there be one for blobs (blob1 and blob2) ? If you need to know this you can iterate over the document, which will give you each key/value pair, and then determine how many of them are blobs. If you find this to be too slow then store the result somewhere in the document and update it when you add or remove blobs. In 1.x blobs (aka attachments) were based on the CouchDB model of having data be special but in 2.x they are the same as any other value type. The only reason you see _attachments at all in server is for compatibility purposes in case some other clients are still running 1.x.

Thank you, just what I want to know for sure.
So it looks like the intention was never store related blobs to each, since retrieving them would be a pain. Better we would just use blob when we want to store like a thumbnail or something, but ideally these images should be store with another service like s3 and just access it with a reference key to that data store. Seems like blobs is just a feature, but not meant to be use to hold many images of let’s say cats related. Because retrieving it would mean you need to know every car name in advance.

Because retrieving it would mean you need to know every car name in advance.

Um … I think you must have overlooked the ability to iterate Dictionary and Array objects. With that, you can build a simple recursive function to find all Blobs in a document.

If that’s not the issue, then I really have no idea what you’re trying to do.

I think the increment approach by borrrden is the appropriate solution here. Initially I would allow client to let’s say add images to a task and for each of those images it would be saved as image_(index).jpg so for 3 we end up with image_0, Image_1 and image_2. Afterwards incrementing the key “imageCount”: 3 in the json doc.

When retrieving it, the collectionviews cells count would be 3 from accessing this doc, so 3 cells would get deque giving us index 0,1,2 and then using blob for key(image_(indexPath.row).jpg) we would individually get the images for each without having to do any recursive calls. This is at least for iOS development, maybe for android or something other development requires different approach. This is just how it’s usually done on iOS. But sure, that’s another approach to. I was just figuring the easiest/fastest way to get the document count, because I’m storing these images name the most generic way I can. I don’t need anything but it’s index to retrieve and display images. Every single task would be saved as images_index.jpg and the task itself would be what’s unique not the images blob name that is link to it. Thanks again

Correction: Every single blobs* not task

Or you could put the blobs into an array…

Exactly! I agree, that’s what i’m saying why we should treat blobs special, so we can iterate over them. Couchbaselite doesn’t have a method on its document to add them to a single place. instead this is what was provided by couchbase doc on the website.

let appleImage = UIImage(named: “avatar.jpg”)!
let imageData = UIImageJPEGRepresentation(appleImage, 1)!

let blob = Blob(contentType: “image/jpeg”, data: imageData)
newTask.setBlob(blob, forKey: “avatar”)
try database.saveDocument(newTask)

if let taskBlob = newTask.blob(forKey: “image”) {
image = UIImage(data: taskBlob.content!)
}

Unless you’re saying to retrieve these blobs (means knowing the names in advance) and add them to array to get count. I need the count ahead of loading the views so at the time of the screen appears, the datasource/delegate already knows what to display. I have to provide these delegate/datasource ahead of time how many exist to appropriate deque these cell for reusuability and efficiency. So yes, you can get all blobs append them to array and call count, but that’s not going to work for these collectionview and tableviews we have in iOS. every ios app you see with a list and images scrolling uses this, every single time.

I believe Ms. Priya Rajagopal creates a lot of Swift projects example and she may know more about iOS/Swift Apple world. I just really felt it was something that is missing and too commonly used in iOS development to not have easy access to. Getting an image/blob count unfortunately even though it seems like most mobile developer can do it on their own, it’s just not too common to create a quick algorithm to provide to your datasource, I hope this comes in future release por favor. And to your point, iOS developers are a bit spoiled, and things just got much easier 2019 , even json parsing is for us is not that complicated, we have an codable protocol that would just get these information back for us if we have our class variables structure same as the json doc.

Why? Just create an array and add the blobs to it. This functionality doesn’t need to be special for blobs. Add an array to your document, then you can add whatever you want inside, including blobs. Blobs do not need to be at the top level of the document. Here is the JSON equivalent:

{
     "otherdata": "foo",
     "images": [
        <blob>
        <blob>
        <blob>
    ]
}

Then to get all the images, just retrieve the array in the images key. You will have both the count (the count of the array itself) and the data (the images in the array) in a way that you don’t have to remember keys for.