---
title: Attachments: Chat App
slug: v4-5/swift/tutorials/chat-app
docTags: 
createdAt: 2023-09-11T19:07:19.136Z
---

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

- [Creating Attachments](./#creating-attachments)
- [Replicating Attachments ](./#replicating-attachments)

:::hint{type="warning"}
**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](docId\:P6OPuBbnA--hc2DCjWshS).
:::

:::hint{type="info"}
For the source code for the demo DittoChat app for iOS in the [demoapp-chat](https://github.com/getditto/demoapp-chat/blob/main/iOS/DittoChat/Data/DittoService.swift#L340-L350) GitHub repository.
:::

### Creating Attachments

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

:::::WorkflowBlock
::::WorkflowBlockItem
From the `createImageMessage()` function following the **Large Image** comment in the demo [DittoChat](https://github.com/getditto/demoapp-chat/blob/main/iOS/DittoChat/Data/DittoService.swift#L340-L350) 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.&#x20;

:::hint{type="info"}
Since the demo DittoChat app does not require end users to upload an avatar image, declare the new attachment as an optional object.
:::
::::

::::WorkflowBlockItem
Call the `set()` function and, using the `DittoMutableDocumentPath` instance,  pass the `DittoAttachment` object&#x20;

:::hint{type="info"}
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).
:::
::::

::::WorkflowBlockItem
Using the `DittoAttachmentToken`, initialize the `attachment` object. &#x20;

:::hint{type="info"}
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](docId\:VoFl0GukXq8AjsMMW7Sgz), 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 asynchronously 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 abort 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)
        }
    }
)
```

