Data Manager
Complete guide to monitoring and controlling data synchronization with the ZyncDataManager.
Table of contents
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