website logo
Legacy DocsPortal
⌘K
Welcome to Ditto
Onboarding
Ditto Basics
SDK Setup Guides
Platform Manual
HTTP API
Kafka Connector
Use Cases
FAQs
Troubleshooting Guide
Support
Docs powered by
Archbee
SDK Setup Guides
...
Swift
Tutorials in Swift

Attachments: Chat App

7min

This tutorial demonstrates how to work with an attachment in a chat app:

  • Creating Attachments
  • Replicating Attachments 

Syncing large documents can significantly impact network performance:

Caution is advised when handling large binary data, such as a high-resolution image or video exceeding 50 megapixels; a deeply-embedded document; or a very large document.

Instead of storing files exceeding 250kb directly within a document object, carefully consider using attachments . For more information, see Attachment Objects.

For the source code for the demo DittoChat app for iOS in the demoapp-chat GitHub repository.

Creating Attachments

The following snippet demonstrates how to create a new attachment in the demo chat app:

1

From the createImageMessage() function following the Large Image comment in the demo DittoChat app for iOS code:

  1. Pass the user instance and the URL path where the avatar image is stored.
  2. Create a new DittoAttachment object by calling the .newAttachment() method.

Since the demo DittoChat app does not require end users to upload an avatar image, declare the new attachment as an optional object.

2

Call the set() function and, using the DittoMutableDocumentPath instance, pass the DittoAttachment object

The DittoMutableDocumentPath instance links the user document object ID to the attachment object and stores the attachment's data bytes and metadata properties to the given local Ditto store (Small Peer).

3

Using the DittoAttachmentToken, initialize the attachment object.

Later in the code, you'll invoke the DittoAttachmentToken to retrieve the attachment data from its current storage location, which could be either the Ditto store or the peer-to-peer mesh network. For more information, see Replicating the Attachment, as follows.

Swift
|
func addAvatar(to user: User, imagePath: String) {
    // optionally, add metadata as [String: String]
    let metadata = [
        "filename": "\(userID)_avatar.png",
        "createdOn": ISO8601DateFormatter().string(from: Date())
    ]
    
    let attachment = collection.newAttachment(
        path: imagePath,
        metadata: metadata
    )!
    
    // add the attachment to the Alice user document
    ditto.store["users"].findByID(user.id).update { mutableDoc in
        mutableDoc?["avatarToken"].set(attachment)
    }
}

Swift
|
func addAvatar(to user: User, imagePath: String) {
    // optionally, add metadata as [String: String]
    let metadata = [
        "filename": "\(userID)_avatar.png",
        "createdOn": ISO8601DateFormatter().string(from: Date())
    ]
    
    let attachment = collection.newAttachment(
        path: imagePath,
        metadata: metadata
    )!
    
    // add the attachment to the Alice user document
    ditto.store["users"].findByID(user.id).update { mutableDoc in
        mutableDoc?["avatarToken"].set(attachment)
    }
}


Note that on the User model, the avatarToken variable is of type DittoAttachmentToken, yet we call the set() function with a DittoAttachment object. This can be confusing. The set() function, called with the attachment on a DittoMutableDocumentPath instance, causes the data bytes of the attachment at the given file location to be stored in the Ditto database, along with the metadata; the document property is initialized with a DittoAttachmentToken with which we will later fetch the attachment data asnynchronously from the peer-to-peer mesh, or from local storage if it has already been fetched.

Replicating Attachments

Peers can now find the document, fetch the attachment, and use the attachment image. If you want to update a progress view, use the progress event value. In the following example, we've wrapped DittoCollection.fetchAttachment(token:deliverOn:onFetchEvent:) in an ImageAttachmentFetcher struct for convenience.

Swift
|
struct ImageAttachmentFetcher {
    var fetcher: DittoAttachmentFetcher?
    
    init(
        ditto: Ditto,
        collection: String,
        token: DittoAttachmentToken,
        onProgress: @escaping (Double) -> Void,
        onComplete: @escaping (Result<UIImage, Error>) -> Void
    ) {
        self.fetcher = ditto.store[collection].fetchAttachment(token: token) { event in
            switch event {
            case .progress(let downloadedBytes, let totalBytes):
                let percent = Double(downloadedBytes) / Double(totalBytes)
                onProgress(percent)
            case .completed(let attachment):
                do {
                    let data = try attachment.getData()
                    if let uiImage = UIImage(data: data) {
                        onComplete(.success(uiImage))
                    }
                } catch {
                    onComplete(.failure(error))
                }
            default:
                print("Error: event case \(event) not handled")
            }
        }
    }
}


Notice that we update a progress view by calling the ImageAttachmentFetcher struct with a progress handling closure and a completion handler for handling the fetched image. Since the attachment fetcher must remain a globally available instance for the entire duration of the asynchronous fetch operation, we maintain a strong reference to the attachment fetcher as indicated with a property.

If, at any time, the attachment fetcher goes out of scope, asynchronous fetch operations silently aborts and the Attachment API fails.

Swift
|
// properties
var attachmentImage: UIImage?
var imageFetcher: ImageAttachmentFetcher?

let doc = collection.findByID(user.id).exec()!
let imageToken = doc["avatarToken"].attachmentToken!

imageFetcher = ImageAttachmentFetcher(ditto: ditto, collection: "users", token: imageToken,
    onProgress: { percentComplete in
        print("Percent complete: \(percentComplete)")
    },
    onComplete: { result in
        switch result {
        case .success(let uiImage):
            attachmentImage = uiImage
        case .failure(let error):
            //handle error
            print(error.localizedDescription)
        }
    }
)




Updated 27 Sep 2023
Did this page help you?
PREVIOUS
UIKit: Task App Quickstart
NEXT
Compatibility with Swift
Docs powered by
Archbee
TABLE OF CONTENTS
Creating Attachments
Replicating Attachments
Docs powered by
Archbee