Data Manager

Complete guide to monitoring and controlling data synchronization with the ZyncDataManager.

Table of contents

  1. Global Upload Status Indicator
    1. 1. Status Monitoring
    2. 2. Detailed Operation List
    3. 3. Live Updates
  2. Module Detail Status Indicator
    1. 1. Status Monitoring
    2. 2. Detailed Operation List
    3. 3. Live Updates
  3. State Types Reference
    1. ZyncUploadState
    2. ZyncUploadStatus

The Zync SDK provides comprehensive data synchronization monitoring through the ZyncDataManager. This manager enables applications to build two key UI patterns for sync awareness: global dashboard indicators and module-specific detail indicators.

Global Upload Status Indicator

The global upload status indicator provides dashboard-level sync awareness across all operations in your application.

1. Status Monitoring

Use zync.dataManager.globalUploadState to monitor overall sync status:

// Monitor global upload state for dashboard indicators
zync.dataManager.globalUploadState.collect { state ->
    when (state) {
        is ZyncUploadState.Idle -> {
            if (state.pendingUploadCount > 0) {
                showSyncIndicator("Pending upload (${state.pendingUploadCount})")
            } else {
                hideSyncIndicator()
            }
        }
        is ZyncUploadState.Uploading -> {
            showSyncIndicator("Uploading (${state.pendingUploadCount})")
        }
        is ZyncUploadState.WaitingForNetwork -> {
            showSyncIndicator("Waiting for internet (${state.pendingUploadCount})")
        }
        is ZyncUploadState.NetworkError -> {
            showSyncIndicator("Network error while uploading (${state.pendingUploadCount})")
        }
        is ZyncUploadState.Error -> {
            showSyncIndicator("Couldn't upload items (${state.pendingUploadCount})")
        }
        is ZyncUploadState.Completed -> {
            showTemporarySyncMessage("All items uploaded successfully", duration = 3000)
        }
    }
}
// Monitor global upload state for dashboard indicators
for await state in zync.dataManager.globalUploadState {
    switch onEnum(of: state) {
    case .idle(let idle):
        if idle.pendingUploadCount > 0 {
            showSyncIndicator("Pending upload (\(idle.pendingUploadCount))")
        } else {
            hideSyncIndicator()
        }
    case .uploading(let uploading):
        showSyncIndicator("Uploading (\(uploading.pendingUploadCount))")
    case .waitingForNetwork(let waiting):
        showSyncIndicator("Waiting for internet (\(waiting.pendingUploadCount))")
    case .networkError(let networkError):
        showSyncIndicator("Network error while uploading (\(networkError.pendingUploadCount))")
    case .error(let error):
        showSyncIndicator("Couldn't upload items (\(error.pendingUploadCount))")
    case .completed:
        showTemporarySyncMessage("All items uploaded successfully", duration: 3.0)
    }
}

2. Detailed Operation List

When the user clicks on the global sync indicator, show a detailed list using zync.dataManager.getPendingUploads():

// Get all pending operations for sync queue UI
val pendingUploads = zync.dataManager.getPendingUploads()

// Build list items for UI display
val listItems = buildList {
    pendingUploads.forEach { upload ->
        // Add main operation item
        add(SyncListItem(
            uploadId = upload.uploadId,
            title = "${upload.entity} ${upload.action}",
            subtitle = upload.entityUid,
            status = upload.status,
            errorMessage = upload.errorMessage,
            isParent = upload.relatedUploads.isNotEmpty(),
            indent = 0
        ))
        
        // Add related operations as child items (e.g., attachments for a job)
        upload.relatedUploads.forEach { relatedUpload ->
            add(SyncListItem(
                uploadId = relatedUpload.uploadId,
                title = "${relatedUpload.entity} ${relatedUpload.action}", // e.g., "ATTACHMENT CREATE"
                subtitle = relatedUpload.entityUid, // e.g., "photo_001.jpg"
                status = relatedUpload.status,
                errorMessage = relatedUpload.errorMessage,
                isParent = false,
                indent = 1 // Indented to show hierarchy
            ))
        }
    }
}

// Display unified sync queue with hierarchy
displaySyncQueue(listItems)
// Get all pending operations for sync queue UI
let pendingUploads = try await zync.dataManager.getPendingUploads()

// Build list items for UI display
var listItems: [SyncListItem] = []
for upload in pendingUploads {
    // Add main operation item
    listItems.append(SyncListItem(
        uploadId: upload.uploadId,
        title: "\(upload.entity) \(upload.action)",
        subtitle: upload.entityUid,
        status: upload.status,
        errorMessage: upload.errorMessage,
        isParent: !upload.relatedUploads.isEmpty,
        indent: 0
    ))
    
    // Add related operations as child items (e.g., attachments for a job)
    for relatedUpload in upload.relatedUploads {
        listItems.append(SyncListItem(
            uploadId: relatedUpload.uploadId,
            title: "\(relatedUpload.entity) \(relatedUpload.action)", // e.g., "ATTACHMENT CREATE"
            subtitle: relatedUpload.entityUid, // e.g., "photo_001.jpg"
            status: relatedUpload.status,
            errorMessage: relatedUpload.errorMessage,
            isParent: false,
            indent: 1 // Indented to show hierarchy
        ))
    }
}

// Display unified sync queue with hierarchy
displaySyncQueue(listItems)

3. Live Updates

Keep the list view reactive using zync.dataManager.operationUpdates to update individual items:

// Update list items in real-time
zync.dataManager.operationUpdates.collect { update ->
    when (update.status) {
        is ZyncUploadStatus.Pending -> {
            updateListItemIndicator(update.uploadId, "waiting to upload")
        }
        is ZyncUploadStatus.Uploading -> {
            if (update.isFileOperation && update.progress != null && update.progress > 0) {
                showCircularProgressIndicator(update.uploadId, update.progress)
            } else {
                showIndefiniteIndicator(update.uploadId)
            }
        }
        is ZyncUploadStatus.Success -> {
            showSuccessCheckmark(update.uploadId)
        }
        is ZyncUploadStatus.NetworkError -> {
            updateListItemIndicator(update.uploadId, "Network error")
        }
        is ZyncUploadStatus.Error -> {
            updateListItemIndicator(update.uploadId, "Error - ${update.status.errorMessage ?: "Unknown error"}")
        }
    }
}
// Update list items in real-time
for await update in zync.dataManager.operationUpdates {
    switch onEnum(of: update.status) {
    case .pending:
        updateListItemIndicator(update.uploadId, "waiting to upload")
    case .uploading:
        if update.isFileOperation && update.progress != nil && update.progress! > 0 {
            showCircularProgressIndicator(update.uploadId, update.progress!)
        } else {
            showIndefiniteIndicator(update.uploadId)
        }
    case .success:
        showSuccessCheckmark(update.uploadId)
    case .networkError:
        updateListItemIndicator(update.uploadId, "Network error")
    case .error(let error):
        updateListItemIndicator(update.uploadId, "Error - \(error.errorMessage ?? "Unknown error")")
    default:
        break
    }
}

Module Detail Status Indicator

The module detail status indicator provides sync awareness for specific business contexts like individual jobs, notes, or attachments.

1. Status Monitoring

Use zync.dataManager.getModuleUploadState(ZuperModule.JOB, jobUid) to monitor module-specific sync status:

// Monitor job-specific sync status
zync.dataManager.getModuleUploadState(ZuperModule.JOB, jobUid).collect { state ->
    when (state) {
        is ZyncUploadState.Idle -> {
            if (state.pendingUploadCount > 0) {
                showJobSyncIndicator("Pending upload (${state.pendingUploadCount})")
            } else {
                hideJobSyncIndicator()
            }
        }
        is ZyncUploadState.Uploading -> {
            showJobSyncIndicator("Uploading (${state.pendingUploadCount})")
        }
        is ZyncUploadState.WaitingForNetwork -> {
            showJobSyncIndicator("Waiting for internet (${state.pendingUploadCount})")
        }
        is ZyncUploadState.NetworkError -> {
            showJobSyncIndicator("Network error while uploading (${state.pendingUploadCount})")
        }
        is ZyncUploadState.Error -> {
            showJobSyncIndicator("Couldn't upload items (${state.pendingUploadCount})")
        }
        is ZyncUploadState.Completed -> {
            showTemporaryJobMessage("All items uploaded successfully", duration = 3000)
        }
    }
}
// Monitor job-specific sync status
for await state in zync.dataManager.getModuleUploadState(module: .job, moduleUid: jobUid) {
    switch onEnum(of: state) {
    case .idle(let idle):
        if idle.pendingUploadCount > 0 {
            showJobSyncIndicator("Pending upload (\(idle.pendingUploadCount))")
        } else {
            hideJobSyncIndicator()
        }
    case .uploading(let uploading):
        showJobSyncIndicator("Uploading (\(uploading.pendingUploadCount))")
    case .waitingForNetwork(let waiting):
        showJobSyncIndicator("Waiting for internet (\(waiting.pendingUploadCount))")
    case .networkError(let networkError):
        showJobSyncIndicator("Network error while uploading (\(networkError.pendingUploadCount))")
    case .error(let error):
        showJobSyncIndicator("Couldn't upload items (\(error.pendingUploadCount))")
    case .completed:
        showTemporaryJobMessage("All items uploaded successfully", duration: 3.0)
    }
}

2. Detailed Operation List

When the user clicks on the module sync indicator, show module-specific operations using zync.dataManager.getModulePendingUploads(module = ZuperModule.JOB, moduleUid = jobUid):

// Get pending operations for a specific job
val jobPendingOps = zync.dataManager.getModulePendingUploads(
    module = ZuperModule.JOB,
    moduleUid = jobUid
)

// Build list items for UI display (same structure as global list)
val listItems = buildList {
    jobPendingOps.forEach { upload ->
        // Add main operation item
        add(SyncListItem(
            uploadId = upload.uploadId,
            title = "${upload.entity} ${upload.action}",
            subtitle = upload.entityUid,
            status = upload.status,
            errorMessage = upload.errorMessage,
            isParent = upload.relatedUploads.isNotEmpty(),
            indent = 0
        ))
        
        // Add related operations as child items (e.g., attachments for this job)
        upload.relatedUploads.forEach { relatedUpload ->
            add(SyncListItem(
                uploadId = relatedUpload.uploadId,
                title = "${relatedUpload.entity} ${relatedUpload.action}", // e.g., "ATTACHMENT CREATE"
                subtitle = relatedUpload.entityUid, // e.g., "photo_001.jpg"
                status = relatedUpload.status,
                errorMessage = relatedUpload.errorMessage,
                isParent = false,
                indent = 1 // Indented to show hierarchy
            ))
        }
    }
}

// Display filtered sync queue with hierarchy
displaySyncQueue(listItems)
// Get pending operations for a specific job
let jobPendingOps = try await zync.dataManager.getModulePendingUploads(
    module: .job,
    moduleUid: jobUid
)

// Build list items for UI display (same structure as global list)
var listItems: [SyncListItem] = []
for upload in jobPendingOps {
    // Add main operation item
    listItems.append(SyncListItem(
        uploadId: upload.uploadId,
        title: "\(upload.entity) \(upload.action)",
        subtitle: upload.entityUid,
        status: upload.status,
        errorMessage: upload.errorMessage,
        isParent: !upload.relatedUploads.isEmpty,
        indent: 0
    ))
    
    // Add related operations as child items (e.g., attachments for this job)
    for relatedUpload in upload.relatedUploads {
        listItems.append(SyncListItem(
            uploadId: relatedUpload.uploadId,
            title: "\(relatedUpload.entity) \(relatedUpload.action)", // e.g., "ATTACHMENT CREATE"
            subtitle: relatedUpload.entityUid, // e.g., "photo_001.jpg"
            status: relatedUpload.status,
            errorMessage: relatedUpload.errorMessage,
            isParent: false,
            indent: 1 // Indented to show hierarchy
        ))
    }
}

// Display filtered sync queue with hierarchy
displaySyncQueue(listItems)

3. Live Updates

Keep the module-specific list view reactive using zync.dataManager.operationUpdates to update individual items:

// Update module-specific list items in real-time
zync.dataManager.operationUpdates.collect { update ->
    // Filter updates relevant to this module
    if (isRelevantToModule(update, ZuperModule.JOB, jobUid)) {
        when (update.status) {
            is ZyncUploadStatus.Pending -> {
                updateModuleListItemIndicator(update.uploadId, "waiting to upload")
            }
            is ZyncUploadStatus.Uploading -> {
                if (update.isFileOperation && update.progress != null && update.progress > 0) {
                    showModuleCircularProgressIndicator(update.uploadId, update.progress)
                } else {
                    showModuleIndefiniteIndicator(update.uploadId)
                }
            }
            is ZyncUploadStatus.Success -> {
                showModuleSuccessCheckmark(update.uploadId)
            }
            is ZyncUploadStatus.NetworkError -> {
                updateModuleListItemIndicator(update.uploadId, "Network error")
            }
            is ZyncUploadStatus.Error -> {
                updateModuleListItemIndicator(update.uploadId, "Error - ${update.status.errorMessage ?: "Unknown error"}")
            }
        }
    }
}
// Update module-specific list items in real-time
for await update in zync.dataManager.operationUpdates {
    // Filter updates relevant to this module
    if isRelevantToModule(update, .job, jobUid) {
        switch onEnum(of: update.status) {
        case .pending:
            updateModuleListItemIndicator(update.uploadId, "waiting to upload")
        case .uploading:
            if update.isFileOperation && update.progress != nil && update.progress! > 0 {
                showModuleCircularProgressIndicator(update.uploadId, update.progress!)
            } else {
                showModuleIndefiniteIndicator(update.uploadId)
            }
        case .success:
            showModuleSuccessCheckmark(update.uploadId)
        case .networkError:
            updateModuleListItemIndicator(update.uploadId, "Network error")
        case .error(let error):
            updateModuleListItemIndicator(update.uploadId, "Error - \(error.errorMessage ?? "Unknown error")")
        default:
            break
        }
    }
}

State Types Reference

ZyncUploadState

  • Idle: No active operations, may have pending operations waiting
  • Uploading: Currently processing operations with pending count
  • WaitingForNetwork: Operations queued but waiting for network connectivity
  • NetworkError: Network-related failures preventing sync progress
  • Error: Non-network errors that require user attention
  • Completed: All operations successfully processed

ZyncUploadStatus

  • Pending: Operation queued for processing
  • Uploading: Operation actively being uploaded
  • Success: Operation completed successfully
  • NetworkError: Operation failed due to network connectivity issues
  • Error(errorMessage): Operation failed and requires attention, includes optional error message

Copyright © 2025 Zuper Inc. All rights reserved. This software is proprietary and confidential.