Service Task Management

API reference for service task operations in the Zync SDK.

Table of contents

  1. Getting Service Tasks
  2. Getting Service Task Details
  3. Updating Service Task Status
    1. Service Task Status Types
    2. Assignment Logic
  4. Real-time Updates
  5. Error Handling
    1. Common Error Types
  6. Offline-First Behavior
    1. Initial Sync vs Cached Data
    2. Background Delta Sync
    3. Network Resilience
  7. Best Practices
    1. Optimal Usage Patterns
      1. Efficient Task Loading
      2. Offline-First Operations
    2. Performance Guidelines
      1. Do’s ✅
      2. Don’ts ❌
    3. Error Handling Best Practices
    4. Security Considerations
  8. Supported Modules

The ServiceTaskManager provides comprehensive offline-first service task management with real-time synchronization. Service tasks are individual work items within jobs that track specific activities, asset inspections, and form submissions with complete audit trails. The manager follows the offline-first approach where local data is returned immediately and background sync operations keep the data updated.

Getting Service Tasks

Retrieve a list of service tasks associated with a specific job or module using offline-first data access:

import zync.api.sync.models.ZuperModule
import zync.api.servicetask.models.GetServiceTasksResult
import zync.api.common.errors.ZyncError

val result = zync.serviceTasks.getServiceTasks(
    module = ZuperModule.JOB,
    moduleUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)

when (result) {
    is GetServiceTasksResult.Success -> {
        val tasks = result.serviceTasks
        println("Found ${tasks.size} service tasks")

        tasks.forEach { task ->
            println("Task: ${task.serviceTaskTitle}")
            println("Status: ${task.serviceTaskStatus}")
            println("Sequence: ${task.sequenceNo}")
            println("Description: ${task.serviceTaskDescription ?: "No description"}")

            // Check assigned users
            if (task.assignedTo.isNotEmpty()) {
                println("Assigned to:")
                task.assignedTo.forEach { assignment ->
                    println("  - ${assignment.user.displayName} (${assignment.team.teamName})")
                }
            }

            // Check asset information
            task.serviceTaskAsset?.let { asset ->
                println("Asset: ${asset.assetName} (${asset.assetCode})")
                println("Asset Status: ${asset.assetStatus}")
            }

            // Check inspection form
            task.inspectionForm?.let { form ->
                println("Inspection Form: ${form.assetFormName}")
                println("Form Fields: ${form.inspectionFormFields.size}")
            }

            println("---")
        }
    }
    is GetServiceTasksResult.Failure -> {
        println("Error: ${result.error.message}")
        when (result.error) {
            is ZyncError.Network -> {
                println("Check your internet connection")
            }
            is ZyncError.Error -> {
                result.error.code?.let { code ->
                    println("Error code: $code")
                }
            }
        }
    }
}
let result = try await zync.serviceTasks.getServiceTasks(
    module: .job,
    moduleUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)

switch onEnum(of: result) {
case .success(let success):
    let tasks = success.serviceTasks
    print("Found \(tasks.count) service tasks")

    for task in tasks {
        print("Task: \(task.serviceTaskTitle)")
        print("Status: \(task.serviceTaskStatus)")
        print("Sequence: \(task.sequenceNo)")
        print("Description: \(task.serviceTaskDescription ?? "No description")")

        // Check assigned users
        if !task.assignedTo.isEmpty {
            print("Assigned to:")
            for assignment in task.assignedTo {
                print("  - \(assignment.user.displayName ?? "") (\(assignment.team.teamName ?? ""))")
            }
        }

        // Check asset information
        if let asset = task.serviceTaskAsset {
            print("Asset: \(asset.assetName) (\(asset.assetCode))")
            print("Asset Status: \(asset.assetStatus?.rawValue ?? "unknown")")
        }

        // Check inspection form
        if let form = task.inspectionForm {
            print("Inspection Form: \(form.assetFormName)")
            print("Form Fields: \(form.inspectionFormFields.count)")
        }

        print("---")
    }
case .failure(let failure):
    print("Error: \(failure.error.message)")
    switch onEnum(of: failure.error) {
    case .network:
        print("Check your internet connection")
    case .error(let error):
        print("Error: \(error.message)")
        if let code = error.code {
            print("Error code: \(code)")
        }
    }
}

Getting Service Task Details

Retrieve detailed information for a specific service task, including status history, assignments, and related data:

import zync.api.servicetask.models.GetServiceTaskDetailResult
import zync.api.common.errors.ZyncError

val result = zync.serviceTasks.getServiceTaskDetail("9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")

when (result) {
    is GetServiceTaskDetailResult.Success -> {
        val taskDetail = result.serviceTaskDetail
        println("Task: ${taskDetail.serviceTaskTitle}")
        println("Status: ${taskDetail.serviceTaskStatus}")
        println("Module: ${taskDetail.module}")
        println("Description: ${taskDetail.serviceTaskDescription ?: "No description"}")

        // Display timing information
        taskDetail.estimateDuration?.let { estimate ->
            println("Estimated Duration: ${estimate.days}d ${estimate.hours}h ${estimate.minutes}m")
        }

        taskDetail.scheduledDuration?.let { scheduled ->
            println("Scheduled Duration: ${scheduled} hours")
        }

        taskDetail.actualDuration?.let { actual ->
            println("Actual Duration: ${actual} hours")
        }

        // Display timing details
        println("Start Time: ${taskDetail.actualStartTime ?: "Not started"}")
        println("End Time: ${taskDetail.actualEndTime ?: "Not completed"}")

        // Display status history
        if (taskDetail.serviceTaskStatusHistory.isNotEmpty()) {
            println("Status History:")
            taskDetail.serviceTaskStatusHistory.forEach { history ->
                println("  - ${history.status} at ${history.createdAt}")
                println("    By: ${history.doneBy.displayName}")
                history.remarks?.let { remarks ->
                    println("    Remarks: $remarks")
                }
            }
        }

        // Display assigned users
        taskDetail.assignedTo?.let { assignments ->
            if (assignments.isNotEmpty()) {
                println("Assigned Users:")
                assignments.forEach { assignment ->
                    println("  - ${assignment.user.displayName} (${assignment.team.teamName})")
                }
            }
        }

        // Display asset information
        taskDetail.serviceTaskAsset?.let { asset ->
            println("Asset Details:")
            println("  Name: ${asset.assetName}")
            println("  Code: ${asset.assetCode}")
            println("  Serial: ${asset.assetSerialNumber ?: "N/A"}")
            println("  Quantity: ${asset.assetQuantity}")
            println("  Status: ${asset.assetStatus}")
            asset.warrantyExpiryDate?.let { warranty ->
                println("  Warranty Expires: $warranty")
            }
        }

        // Display inspection form information
        taskDetail.inspectionForm?.let { form ->
            println("Inspection Form:")
            println("  Name: ${form.assetFormName}")
            println("  Description: ${form.description ?: "No description"}")
            println("  Active: ${form.isActive}")
            println("  Fields: ${form.inspectionFormFields.size}")

            // Show form submission info
            taskDetail.inspectionFormSubmissionUid?.let { submissionUid ->
                println("  Submission UID: $submissionUid")
            }

            taskDetail.latestInspectionFormSubmissionTimestamp?.let { timestamp ->
                println("  Latest Submission: $timestamp")
            }
        }
    }
    is GetServiceTaskDetailResult.Failure -> {
        println("Error: ${result.error.message}")
        when (result.error) {
            is ZyncError.Network -> {
                println("Check your internet connection")
            }
            is ZyncError.Error -> {
                result.error.code?.let { code ->
                    println("Error code: $code")
                }
            }
        }
    }
}
let result = try await zync.serviceTasks.getServiceTaskDetail(serviceTaskUid: "9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")

switch onEnum(of: result) {
case .success(let success):
    let taskDetail = success.serviceTaskDetail
    print("Task: \(taskDetail.serviceTaskTitle)")
    print("Status: \(taskDetail.serviceTaskStatus)")
    print("Module: \(taskDetail.module)")
    print("Description: \(taskDetail.serviceTaskDescription ?? "No description")")

    // Display timing information
    if let estimate = taskDetail.estimateDuration {
        print("Estimated Duration: \(estimate.days)d \(estimate.hours)h \(estimate.minutes)m")
    }

    if let scheduled = taskDetail.scheduledDuration {
        print("Scheduled Duration: \(scheduled) hours")
    }

    if let actual = taskDetail.actualDuration {
        print("Actual Duration: \(actual) hours")
    }

    // Display timing details
    print("Start Time: \(taskDetail.actualStartTime ?? "Not started")")
    print("End Time: \(taskDetail.actualEndTime ?? "Not completed")")

    // Display status history
    if !taskDetail.serviceTaskStatusHistory.isEmpty {
        print("Status History:")
        for history in taskDetail.serviceTaskStatusHistory {
            print("  - \(history.status) at \(history.createdAt ?? "")")
            print("    By: \(history.doneBy.displayName ?? "")")
            if let remarks = history.remarks {
                print("    Remarks: \(remarks)")
            }
        }
    }

    // Display assigned users
    if let assignments = taskDetail.assignedTo, !assignments.isEmpty {
        print("Assigned Users:")
        for assignment in assignments {
            print("  - \(assignment.user.displayName ?? "") (\(assignment.team.teamName ?? ""))")
        }
    }

    // Display asset information
    if let asset = taskDetail.serviceTaskAsset {
        print("Asset Details:")
        print("  Name: \(asset.assetName)")
        print("  Code: \(asset.assetCode)")
        print("  Serial: \(asset.assetSerialNumber ?? "N/A")")
        print("  Quantity: \(asset.assetQuantity)")
        print("  Status: \(asset.assetStatus?.rawValue ?? "unknown")")
        if let warranty = asset.warrantyExpiryDate {
            print("  Warranty Expires: \(warranty)")
        }
    }

    // Display inspection form information
    if let form = taskDetail.inspectionForm {
        print("Inspection Form:")
        print("  Name: \(form.assetFormName)")
        print("  Description: \(form.description ?? "No description")")
        print("  Active: \(form.isActive)")
        print("  Fields: \(form.inspectionFormFields.count)")

        // Show form submission info
        if let submissionUid = taskDetail.inspectionFormSubmissionUid {
            print("  Submission UID: \(submissionUid)")
        }

        if let timestamp = taskDetail.latestInspectionFormSubmissionTimestamp {
            print("  Latest Submission: \(timestamp)")
        }
    }
case .failure(let failure):
    print("Error: \(failure.error.message)")
    switch onEnum(of: failure.error) {
    case .network:
        print("Check your internet connection")
    case .error(let error):
        print("Error: \(error.message)")
        if let code = error.code {
            print("Error code: \(code)")
        }
    }
}

Updating Service Task Status

Update service task status with optional user assignment support. This method provides complete offline-first service task status management with assignment capabilities:

import zync.api.servicetask.models.ZyncServiceTaskStatusUpdateData
import zync.api.servicetask.models.ZyncServiceTaskStatus
import zync.api.servicetask.models.UpdateServiceTaskStatusResult
import zync.api.common.errors.ZyncError

// Simple status update
val statusUpdate = ZyncServiceTaskStatusUpdateData(
    jobUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    serviceTaskUid = "9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f",
    serviceTaskStatus = ZyncServiceTaskStatus.IN_PROGRESS,
    remarks = "Started working on the equipment inspection"
)

// Status update with assignment
val statusUpdateWithAssignment = ZyncServiceTaskStatusUpdateData(
    jobUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    serviceTaskUid = "9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f",
    serviceTaskStatus = ZyncServiceTaskStatus.COMPLETED,
    remarks = "Equipment inspection completed successfully",
    assignToTask = true,  // Assign current user to this task
    assignToJob = true,   // Also assign current user to the job
    assignFromTeam = "team-uid-123" // Assign from specific team
)

val result = zync.serviceTasks.updateServiceTaskStatus(statusUpdateWithAssignment)

when (result) {
    is UpdateServiceTaskStatusResult.Success -> {
        println("Service task status updated successfully")
        // Status is immediately available locally and will sync to server
    }
    is UpdateServiceTaskStatusResult.Failure -> {
        println("Failed to update status: ${result.error.message}")
        when (result.error) {
            is ZyncError.Network -> {
                println("Update saved locally, will sync when online")
            }
            is ZyncError.Error -> {
                result.error.code?.let { code ->
                    println("Error code: $code")
                }
            }
        }
    }
}
// Simple status update
let statusUpdate = ZyncServiceTaskStatusUpdateData(
    jobUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    serviceTaskUid: "9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f",
    serviceTaskStatus: .inProgress,
    remarks: "Started working on the equipment inspection",
    assignToTask: false,
    assignToJob: false,
    assignFromTeam: nil
)

// Status update with assignment
let statusUpdateWithAssignment = ZyncServiceTaskStatusUpdateData(
    jobUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    serviceTaskUid: "9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f",
    serviceTaskStatus: .completed,
    remarks: "Equipment inspection completed successfully",
    assignToTask: true,  // Assign current user to this task
    assignToJob: true,   // Also assign current user to the job
    assignFromTeam: "team-uid-123" // Assign from specific team
)

let result = try await zync.serviceTasks.updateServiceTaskStatus(serviceTaskStatusUpdateData: statusUpdateWithAssignment)

switch onEnum(of: result) {
case .success:
    print("Service task status updated successfully")
    // Status is immediately available locally and will sync to server
case .failure(let failure):
    print("Failed to update status: \(failure.error.message)")
    switch onEnum(of: failure.error) {
    case .network:
        print("Update saved locally, will sync when online")
    case .error(let error):
        print("Error: \(error.message)")
        if let code = error.code {
            print("Error code: \(code)")
        }
    }
}

Service Task Status Types

The available service task statuses are:

  • OPEN: Task is created and ready to be worked on
  • IN_PROGRESS: Task is currently being worked on
  • ON_HOLD: Task is temporarily paused
  • COMPLETED: Task has been finished successfully
  • CANCELED: Task has been canceled
  • INCOMPLETE: Task was not completed properly

Assignment Logic

The status update supports flexible assignment options:

  • assignToTask: Assigns the current user to this specific service task
  • assignToJob: Assigns the current user to the parent job
  • assignFromTeam: Specifies which team the assignment should be from (optional)

Real-time Updates

Monitor service task changes in real-time using the reactive Flow API:

import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.collect
import zync.api.servicetask.models.ServiceTaskChangeEvent
import zync.api.sync.models.ZuperModule

// Collect service task changes with real-time updates
val job = launch {
    zync.serviceTasks.observeServiceTaskChanges(
        module = ZuperModule.JOB,
        moduleUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    ).collect { changeEvent ->
        when (changeEvent) {
            is ServiceTaskChangeEvent.ServiceTasksUpdated -> {
                val updatedTasks = changeEvent.updatedServiceTasks
                val deletedTaskUids = changeEvent.deletedServiceTaskUids

                // Handle deletions first - remove from current list
                deletedTaskUids.forEach { deletedUid ->
                    currentTaskList.removeAll { it.serviceTaskUid == deletedUid }
                    println("Removed deleted task: $deletedUid")
                }

                // Handle updates and additions
                updatedTasks.forEach { updatedTask ->
                    val existingIndex = currentTaskList.indexOfFirst {
                        it.serviceTaskUid == updatedTask.serviceTaskUid
                    }
                    if (existingIndex != -1) {
                        // Update existing task
                        currentTaskList[existingIndex] = updatedTask
                        println("Updated existing task: ${updatedTask.serviceTaskTitle}")
                    } else {
                        // Add new task
                        currentTaskList.add(updatedTask)
                        println("Added new task: ${updatedTask.serviceTaskTitle}")
                    }
                }

                // Show notification for changes
                val totalChanges = updatedTasks.size + deletedTaskUids.size
                if (totalChanges > 0) {
                    showUpdateAvailableChip("$totalChanges service task changes available")
                }

                // Refresh UI with updated list
                notifyTaskListChanged()
            }
            is ServiceTaskChangeEvent.ServiceTaskUploadStatusChanged -> {
                val taskUid = changeEvent.serviceTaskUid
                val uploadStatus = changeEvent.uploadStatus

                // Find existing task in your list
                val existingTask = currentTaskList.find { it.serviceTaskUid == taskUid }

                if (existingTask != null) {
                    println("Upload status changed for ${existingTask.serviceTaskTitle}: $uploadStatus")

                    // Update UI to show upload progress or status
                    notifyTaskItemChanged(taskUid)
                }
            }
        }
    }
}

// Cancel when done
job.cancel()
// Observe service task changes using SKIE's AsyncSequence support
let task = Task {
    for await changeEvent in zync.serviceTasks.observeServiceTaskChanges(
        module: .job,
        moduleUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    ) {
        switch changeEvent {
        case .serviceTasksUpdated(let updateEvent):
            let updatedTasks = updateEvent.updatedServiceTasks
            let deletedTaskUids = updateEvent.deletedServiceTaskUids

            // Handle deletions first - remove from current list
            for deletedUid in deletedTaskUids {
                currentTaskList.removeAll { $0.serviceTaskUid == deletedUid }
                print("Removed deleted task: \(deletedUid)")
            }

            // Handle updates and additions
            for updatedTask in updatedTasks {
                if let existingIndex = currentTaskList.firstIndex(where: {
                    $0.serviceTaskUid == updatedTask.serviceTaskUid
                }) {
                    // Update existing task
                    currentTaskList[existingIndex] = updatedTask
                    print("Updated existing task: \(updatedTask.serviceTaskTitle)")
                } else {
                    // Add new task
                    currentTaskList.append(updatedTask)
                    print("Added new task: \(updatedTask.serviceTaskTitle)")
                }
            }

            // Show notification for changes
            let totalChanges = updatedTasks.count + deletedTaskUids.count
            if totalChanges > 0 {
                await showUpdateAvailableChip("\(totalChanges) service task changes available")
            }

            // Refresh UI with updated list
            await notifyTaskListChanged()
        case .serviceTaskUploadStatusChanged(let statusEvent):
            let taskUid = statusEvent.serviceTaskUid
            let uploadStatus = statusEvent.uploadStatus

            // Find existing task in your list
            if let existingTask = currentTaskList.first(where: { $0.serviceTaskUid == taskUid }) {
                print("Upload status changed for \(existingTask.serviceTaskTitle): \(uploadStatus)")

                // Update UI to show upload progress or status
                notifyTaskItemChanged(taskUid)
            }
        }
    }
}

// Cancel when done
task.cancel()

Error Handling

Common Error Types

when (result) {
    is GetServiceTasksResult.Success -> {
        // Handle success
        val tasks = result.serviceTasks
        displayTasks(tasks)
    }
    is GetServiceTasksResult.Failure -> {
        when (result.error) {
            is ZyncError.Network -> {
                println("Network error - showing cached data")
                // Show offline indicator but continue with cached data
                showOfflineIndicator()
                loadCachedTasks()
            }
            is ZyncError.Error -> {
                println("API error: ${result.error.message}")
                result.error.code?.let { code ->
                    when (code) {
                        401 -> showLoginPrompt()
                        403 -> showAccessDeniedMessage()
                        404 -> showNoTasksMessage()
                        else -> showGenericErrorMessage()
                    }
                }
            }
        }
    }
}
switch onEnum(of: result) {
case .success(let success):
    // Handle success
    let tasks = success.serviceTasks
    displayTasks(tasks)
case .failure(let failure):
    switch onEnum(of: failure.error) {
    case .network:
        print("Network error - showing cached data")
        // Show offline indicator but continue with cached data
        showOfflineIndicator()
        loadCachedTasks()
    case .error(let error):
        print("API error: \(error.message)")
        if let code = error.code {
            switch code {
            case 401:
                showLoginPrompt()
            case 403:
                showAccessDeniedMessage()
            case 404:
                showNoTasksMessage()
            default:
                showGenericErrorMessage()
            }
        }
    }
}

Offline-First Behavior

The Zync SDK implements a sophisticated offline-first pattern for service tasks, ensuring optimal performance and user experience regardless of network conditions.

Initial Sync vs Cached Data

First Time Access:

  • Performs synchronous initial sync from server
  • Creates sync metadata to track sync state
  • Returns complete data set after successful sync
  • Marks initial sync as complete for future optimizations

Subsequent Access:

  • Returns cached data immediately (no waiting)
  • Triggers background delta sync for updated data
  • Updates cache silently without blocking user interface
  • Uses last sync timestamp to fetch only changed tasks

Background Delta Sync

// First call - performs initial sync
val initialResult = zync.serviceTasks.getServiceTasks(
    module = ZuperModule.JOB,
    moduleUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)
// User waits for sync to complete

// Subsequent calls - immediate response + background sync
val cachedResult = zync.serviceTasks.getServiceTasks(
    module = ZuperModule.JOB,
    moduleUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)
// User gets immediate response with cached data
// SDK performs background delta sync for any updates since last sync
// First call - performs initial sync
let initialResult = try await zync.serviceTasks.getServiceTasks(
    module: .job,
    moduleUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)
// User waits for sync to complete

// Subsequent calls - immediate response + background sync
let cachedResult = try await zync.serviceTasks.getServiceTasks(
    module: .job,
    moduleUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)
// User gets immediate response with cached data
// SDK performs background delta sync for any updates since last sync

Network Resilience

Offline Operations:

  • All status updates work offline and queue for sync
  • Local database serves as single source of truth
  • Changes are preserved and synced when connectivity returns

Online Operations:

  • Background sync runs automatically without blocking UI
  • Smart conflict resolution for concurrent modifications
  • Automatic retry with exponential backoff for failed operations

Performance Benefit: After initial sync, users get instant responses while the SDK keeps data fresh in the background. This provides the best of both worlds - immediate responsiveness and up-to-date data.

Best Practices

Optimal Usage Patterns

Efficient Task Loading

class ServiceTaskViewModel {
    private var cachedTasks: List<ZyncServiceTask> = emptyList()
    private var isInitialLoad = true

    suspend fun loadServiceTasks() {
        val result = zync.serviceTasks.getServiceTasks(
            module = ZuperModule.JOB,
            moduleUid = jobUid
        )

        when (result) {
            is GetServiceTasksResult.Success -> {
                cachedTasks = result.serviceTasks
                // First load takes longer (initial sync)
                // Subsequent loads are instant (cached + background sync)
                isInitialLoad = false
                updateUI(result.serviceTasks)
            }
            is GetServiceTasksResult.Failure -> {
                handleError(result.error)
            }
        }
    }

    // Use real-time updates for better UX
    suspend fun observeTaskChanges() {
        zync.serviceTasks.observeServiceTaskChanges(ZuperModule.JOB, jobUid)
            .collect { event ->
                updateUI(event)
            }
    }
}

Offline-First Operations

// Update task status offline - it'll sync automatically
suspend fun completeTask(taskUid: String, remarks: String) {
    val statusUpdate = ZyncServiceTaskStatusUpdateData(
        jobUid = currentJobUid,
        serviceTaskUid = taskUid,
        serviceTaskStatus = ZyncServiceTaskStatus.COMPLETED,
        remarks = remarks,
        assignToTask = true, // Take ownership
        assignToJob = false
    )

    val result = zync.serviceTasks.updateServiceTaskStatus(statusUpdate)
    // Status is immediately available locally
    // Will sync to server when connectivity is available
}

Performance Guidelines

Do’s ✅

  • Let the SDK manage sync timing automatically
  • Use cached data for immediate UI responsiveness
  • Update task status optimistically (offline-first)
  • Use real-time updates for keeping UI fresh
  • Embrace background sync for keeping data current

Don’ts ❌

  • Don’t force manual sync unless absolutely necessary
  • Don’t ignore offline capabilities - embrace offline-first patterns
  • Don’t block UI while waiting for sync operations

Error Handling Best Practices

suspend fun handleServiceTaskOperations() {
    try {
        val result = zync.serviceTasks.getServiceTasks(module, moduleUid)
        when (result) {
            is GetServiceTasksResult.Success -> {
                // Handle success
                displayTasks(result.serviceTasks)
            }
            is GetServiceTasksResult.Failure -> {
                when (result.error) {
                    is ZyncError.Network -> {
                        // Show offline indicator but continue with cached data
                        showOfflineIndicator()
                        loadCachedData()
                    }
                    is ZyncError.Error -> {
                        // Log error and show user-friendly message
                        logError(result.error)
                        showErrorMessage("Unable to sync tasks. Using cached data.")
                        loadCachedData()
                    }
                }
            }
        }
    } catch (exception: Exception) {
        // Handle unexpected errors gracefully
        logError(exception)
        showGenericErrorMessage()
    }
}

Security Considerations

  • Offline Security: Service tasks are stored securely in encrypted local database
  • Sync Security: All network operations use secure connections
  • Data Privacy: Assignment and status data are handled according to privacy settings

Key Takeaway: Embrace the offline-first pattern. The SDK is designed to work seamlessly offline with automatic sync - don’t fight this design by forcing online operations.

Supported Modules

Currently, the ServiceTaskManager supports:

  • ZuperModule.JOB: Job-related service tasks

Note: More modules will be supported in future releases.


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