Note Management
Comprehensive guide to using the Zync SDK’s note management features.
Table of contents
- Add Note
- Update Note
- Get Note Detail
- Get Notes
- Delete Note
- Refresh Note Data
- Offline-First Behavior
- Performance and Sync Optimizations
- Best Practices
The ZyncNotesManager provides comprehensive offline-first note management with advanced sync capabilities. It features immediate local storage with background synchronization, smart conflict resolution for offline edits, automatic retry mechanisms, and real-time data access with cached responses. All operations follow the offline-first pattern where local data serves as the primary source of truth, with seamless background synchronization when online.
Add Note
import zync.public.notes.models.ZyncAddNoteContent
import zync.public.notes.models.ZyncNoteVisibilityType
import zync.public.notes.models.ZyncNotePrimaryModule
import zync.public.notes.models.ZyncNoteAssociatedJob
import zync.public.notes.models.ZyncNoteAttachment
import zync.public.notes.models.AddNoteResult
import zync.public.notes.models.UpdateNoteResult
import zync.public.notes.models.GetNoteResult
import zync.public.notes.models.NotesResult
import zync.public.notes.models.DeleteNoteResult
import zync.public.notes.models.ZyncUpdateNoteContent
import zync.api.common.errors.ZyncError
val noteContent = ZyncAddNoteContent(
note = "Equipment inspection completed successfully",
noteType = "TEXT",
canNotifyOthers = true,
noteVisibilityType = ZyncNoteVisibilityType.PUBLIC,
attachments = listOf(
ZyncNoteAttachment(
attachmentUid = "f47ac10b-58cc-4372-a567-0e02b2c3d479",
attachment = "file:///path/to/inspection_photo.jpg",
attachmentName = "inspection_photo.jpg",
attachmentType = "IMAGE",
attachmentSize = 2048576L,
attachmentDescription = "Equipment inspection photo",
tags = listOf("inspection", "equipment"),
thumbnailUrl = null
)
),
userMentions = listOf("user-123-uid", "user-456-uid"),
associatedJob = ZyncNoteAssociatedJob(
jobUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
jobNumber = "JOB-2024-001"
),
associatedCustomer = null,
associatedOrganization = null,
associatedProperty = null,
associatedAsset = null,
associatedQuote = null,
associatedInvoice = null,
associatedContract = null,
associatedRequest = null,
associatedProject = null,
primaryModule = ZyncNotePrimaryModule(
module = "job",
moduleUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
),
isPinned = false,
geoCoordinates = listOf(37.7749, -122.4194) // Optional coordinates
)
val result = zync.notes.addNote(content = noteContent)
when (result) {
is AddNoteResult.Success -> {
val note = result.note
println("Note created: ${note.noteUid}")
println("Content: ${note.note}")
println("Created by: ${note.createdBy?.displayName}")
}
is AddNoteResult.Failure -> {
println("Failed to create note: ${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 noteContent = ZyncAddNoteContent(
note: "Equipment inspection completed successfully",
noteType: "TEXT",
canNotifyOthers: true,
noteVisibilityType: .public,
attachments: [
ZyncNoteAttachment(
attachmentUid: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
attachment: "file:///path/to/inspection_photo.jpg",
attachmentName: "inspection_photo.jpg",
attachmentType: "IMAGE",
attachmentSize: 2048576,
attachmentDescription: "Equipment inspection photo",
tags: ["inspection", "equipment"],
thumbnailUrl: nil
)
],
userMentions: ["user-123-uid", "user-456-uid"],
associatedJob: ZyncNoteAssociatedJob(
jobUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
jobNumber: "JOB-2024-001"
),
associatedCustomer: nil,
associatedOrganization: nil,
associatedProperty: nil,
associatedAsset: nil,
associatedQuote: nil,
associatedInvoice: nil,
associatedContract: nil,
associatedRequest: nil,
associatedProject: nil,
primaryModule: ZyncNotePrimaryModule(
module: "job",
moduleUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
),
isPinned: false,
geoCoordinates: [37.7749, -122.4194] // Optional coordinates
)
let result = try await zync.notes.addNote(content: noteContent)
switch onEnum(of: result) {
case .success(let success):
let note = success.note
print("Note created: \(note.noteUid)")
print("Content: \(note.note ?? "")")
print("Created by: \(note.createdBy?.displayName ?? "Unknown")")
case .failure(let failure):
print("Failed to create note: \(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)")
}
}
}
Update Note
import zync.public.notes.models.ZyncUpdateNoteContent
import zync.public.notes.models.ZyncNoteAttachment
import zync.public.notes.models.ZyncNoteVisibilityType
import zync.public.notes.models.ZyncNoteAssociatedJob
import zync.public.notes.models.UpdateNoteResult
import zync.api.common.errors.ZyncError
val updateContent = ZyncUpdateNoteContent(
noteUid = "9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f",
note = "Updated: Equipment inspection completed with minor issues found",
noteType = "TEXT",
noteVisibilityType = ZyncNoteVisibilityType.PUBLIC,
attachments = listOf(
ZyncNoteAttachment(
attachmentUid = "f47ac10b-58cc-4372-a567-0e02b2c3d479",
attachment = "file:///path/to/inspection_photo.jpg",
attachmentName = "inspection_photo.jpg",
attachmentType = "IMAGE",
attachmentSize = 2048576L,
attachmentDescription = "Equipment inspection photo",
tags = listOf("inspection", "equipment"),
thumbnailUrl = null
),
ZyncNoteAttachment(
attachmentUid = "a12b3c4d-5e6f-7890-abcd-ef1234567890",
attachment = "file:///path/to/issue_report.pdf",
attachmentName = "issue_report.pdf",
attachmentType = "DOCUMENT",
attachmentSize = 1048576L,
attachmentDescription = "Issue report document",
tags = listOf("report", "issue"),
thumbnailUrl = null
)
),
userMentions = listOf("user-123-uid", "supervisor-uid"),
associatedJob = ZyncNoteAssociatedJob(
jobUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
jobNumber = "JOB-2024-001"
),
associatedCustomer = null,
associatedOrganization = null,
associatedProperty = null,
associatedAsset = null,
associatedQuote = null,
associatedInvoice = null,
associatedContract = null,
associatedRequest = null,
associatedProject = null,
geoCoordinates = listOf(37.7749, -122.4194),
isV2Note = true,
isPinned = true
)
val result = zync.notes.updateNote(content = updateContent)
when (result) {
is UpdateNoteResult.Success -> {
val updatedNote = result.note
println("Note updated: ${updatedNote.noteUid}")
println("New content: ${updatedNote.note}")
println("Is pinned: ${updatedNote.isPinned}")
}
is UpdateNoteResult.Failure -> {
println("Failed to update note: ${result.error.message}")
}
}
let updateContent = ZyncUpdateNoteContent(
noteUid: "9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f",
note: "Updated: Equipment inspection completed with minor issues found",
noteType: "TEXT",
noteVisibilityType: .public,
attachments: [
ZyncNoteAttachment(
attachmentUid: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
attachment: "file:///path/to/inspection_photo.jpg",
attachmentName: "inspection_photo.jpg",
attachmentType: "IMAGE",
attachmentSize: 2048576,
attachmentDescription: "Equipment inspection photo",
tags: ["inspection", "equipment"],
thumbnailUrl: nil
),
ZyncNoteAttachment(
attachmentUid: "a12b3c4d-5e6f-7890-abcd-ef1234567890",
attachment: "file:///path/to/issue_report.pdf",
attachmentName: "issue_report.pdf",
attachmentType: "DOCUMENT",
attachmentSize: 1048576,
attachmentDescription: "Issue report document",
tags: ["report", "issue"],
thumbnailUrl: nil
)
],
userMentions: ["user-123-uid", "supervisor-uid"],
associatedJob: ZyncNoteAssociatedJob(
jobUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
jobNumber: "JOB-2024-001"
),
associatedCustomer: nil,
associatedOrganization: nil,
associatedProperty: nil,
associatedAsset: nil,
associatedQuote: nil,
associatedInvoice: nil,
associatedContract: nil,
associatedRequest: nil,
associatedProject: nil,
geoCoordinates: [37.7749, -122.4194],
isV2Note: true,
isPinned: true
)
let result = try await zync.notes.updateNote(content: updateContent)
switch onEnum(of: result) {
case .success(let success):
let updatedNote = success.note
print("Note updated: \(updatedNote.noteUid)")
print("New content: \(updatedNote.note ?? "")")
print("Is pinned: \(updatedNote.isPinned)")
case .failure(let failure):
print("Failed to update note: \(failure.error.message)")
}
Get Note Detail
val result = zync.notes.getNoteDetail("9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")
when (result) {
is GetNoteResult.Success -> {
val note = result.note
println("Note found: ${note.note}")
println("Created: ${note.createdAt}")
println("Last updated: ${note.updatedAt}")
println("Attachments: ${note.attachments?.size ?: 0}")
println("User mentions: ${note.userMentions?.size ?: 0}")
// Access associated objects
note.associatedJob?.let { job ->
println("Associated job: ${job.jobNumber}")
}
// Access creator information
note.createdBy?.let { creator ->
println("Created by: ${creator.displayName}")
}
}
is GetNoteResult.Failure -> {
println("Error retrieving note: ${result.error.message}")
}
}
let result = try await zync.notes.getNoteDetail("9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")
switch onEnum(of: result) {
case .success(let success):
let note = success.note
print("Note found: \(note.note ?? "")")
print("Created: \(note.createdAt)")
print("Last updated: \(note.updatedAt)")
print("Attachments: \(note.attachments?.count ?? 0)")
print("User mentions: \(note.userMentions?.count ?? 0)")
// Access associated objects
if let job = note.associatedJob {
print("Associated job: \(job.jobNumber ?? "")")
}
// Access creator information
if let creator = note.createdBy {
print("Created by: \(creator.displayName ?? "")")
}
case .failure(let failure):
print("Error retrieving note: \(failure.error.message)")
}
Get Notes
import zync.api.sync.models.ZuperModule
import zync.public.notes.models.NotesResult
import zync.api.common.errors.ZyncError
val result = zync.notes.getNotes(
module = ZuperModule.JOB,
moduleUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)
when (result) {
is NotesResult.Success -> {
val notes = result.notes
val totalCount = result.totalCount
println("Loaded ${notes.size} notes (Total: $totalCount)")
notes.forEach { note ->
println("📝 ${note.note ?: "No content"}")
println(" Type: ${note.noteType}")
println(" Created: ${note.createdAt}")
println(" Last updated: ${note.updatedAt}")
println(" Pinned: ${note.isPinned}")
println(" Visibility: ${note.noteVisibilityType}")
// Show attachments
note.attachments?.let { attachments ->
if (attachments.isNotEmpty()) {
println(" Attachments (${attachments.size}):")
attachments.forEach { attachment ->
println(" - ${attachment.attachmentName}")
}
}
}
// Show user mentions
note.userMentions?.let { mentions ->
if (mentions.isNotEmpty()) {
println(" Mentions: ${mentions.joinToString(", ")}")
}
}
println(" ---")
}
}
is NotesResult.Failure -> {
println("Error loading notes: ${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.notes.getNotes(
module: .job,
moduleUid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)
switch onEnum(of: result) {
case .success(let success):
let notes = success.notes
let totalCount = success.totalCount
print("Loaded \(notes.count) notes (Total: \(totalCount))")
for note in notes {
print("📝 \(note.note ?? "No content")")
print(" Type: \(note.noteType)")
print(" Created: \(note.createdAt)")
print(" Last updated: \(note.updatedAt)")
print(" Pinned: \(note.isPinned)")
print(" Visibility: \(note.noteVisibilityType)")
// Show attachments
if let attachments = note.attachments, !attachments.isEmpty {
print(" Attachments (\(attachments.count)):")
for attachment in attachments {
print(" - \(attachment.attachmentName ?? "")")
}
}
// Show user mentions
if let mentions = note.userMentions, !mentions.isEmpty {
print(" Mentions: \(mentions.joined(separator: ", "))")
}
print(" ---")
}
case .failure(let failure):
print("Error loading notes: \(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)")
}
}
}
Delete Note
The SDK implements intelligent delete logic that handles notes differently based on their origin and sync status.
Smart Delete Behavior
Locally Created Notes (Hard Delete):
- Notes created offline that haven’t been synced to server
- Immediately removed from local database
- No server synchronization needed
- Pending CREATE operations are cancelled
Server-Synced Notes (Soft Delete):
- Notes that exist on the server
- Marked as deleted locally and queued for server sync
- Preserves data integrity during offline periods
- Synchronized to server when connectivity is available
Delete Examples
val result = zync.notes.deleteNote("9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")
when (result) {
is DeleteNoteResult.Success -> {
println("Note deleted successfully")
// Note: The SDK automatically determined whether to use
// hard delete (local note) or soft delete (server-synced note)
}
is DeleteNoteResult.Failure -> {
println("Failed to delete note: ${result.error.message}")
}
}
let result = try await zync.notes.deleteNote("9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")
switch onEnum(of: result) {
case .success:
print("Note deleted successfully")
// Note: The SDK automatically determined whether to use
// hard delete (local note) or soft delete (server-synced note)
case .failure(let failure):
print("Failed to delete note: \(failure.error.message)")
}
Delete Logic Flow
- Check Note Origin: SDK determines if note was created locally or exists on server
- Local Notes: Hard delete from database + cancel pending operations
- Server Notes: Mark as deleted + record DELETE operation for sync
- Cleanup: Remove associated attachments and relationships as needed
Automatic Behavior: You don’t need to worry about which delete strategy to use - the SDK automatically chooses the appropriate method based on the note’s sync status.
Refresh Note Data
Mark Note as Stale
Use this method to mark a note as stale, which triggers a refresh on the next access to [getNoteDetail]. The method marks the specified note as needing refresh, causing the next call to fetch the latest version from the server and update the local cache. This is particularly useful after making server-side changes (like updating note visibility) or when you need to ensure fresh data for critical operations.
// Mark note as stale to trigger refresh on next access
zync.notes.markNoteAsStale("9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")
println("Note marked as stale - will fetch latest data on next access")
// The note will be refreshed from server on next getNoteDetail call
// Subsequent calls to getNoteDetail will return the refreshed data
val refreshedNote = zync.notes.getNoteDetail("9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")
// Mark note as stale to trigger refresh on next access
await zync.notes.markNoteAsStale("9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")
print("Note marked as stale - will fetch latest data on next access")
// The note will be refreshed from server on next getNoteDetail call
// Subsequent calls to getNoteDetail will return the refreshed data
let refreshedNote = try await zync.notes.getNoteDetail("9e4f2c8d-1a3b-4c5d-8e9f-2a3b4c5d6e7f")
When to Use: Call
markNoteAsStaleafter making server-side changes that don’t go through the SDK (like API calls to change visibility), when you suspect the local cache may be outdated, or after receiving push notifications about note updates.
Offline-First Behavior
The Zync SDK implements a sophisticated offline-first pattern for notes, 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 notes
Background Delta Sync
// First call - performs initial sync
val initialResult = zync.notes.getNotes(
module = ZuperModule.JOB,
moduleUid = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
)
// User waits for sync to complete
// Subsequent calls - immediate response + background sync
val cachedResult = zync.notes.getNotes(
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.notes.getNotes(
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.notes.getNotes(
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
Sync State Management
The SDK automatically tracks sync metadata for each module:
- Sync Status: IN_PROGRESS, COMPLETED, or ERROR
- Last Sync Time: Timestamp for delta sync filtering
- Total Count: Cached count for immediate responses
- Error Count: Automatic retry logic for failed syncs
- Initial Sync Flag: Optimization flag for first-time vs subsequent calls
Network Resilience
Offline Operations:
- All CRUD operations 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.
Performance and Sync Optimizations
The SDK includes several advanced optimizations to ensure optimal performance and efficient data synchronization.
Bulk Loading Optimizations
Attachment Loading:
- Notes with multiple attachments are loaded using optimized bulk queries
- Attachment tags are populated in batch operations
- Minimizes database round trips for better performance
Relationship Management:
- Note-attachment relationships are handled in bulk
- Duplicate relationships are automatically cleaned up
- Smart relationship diffing for updates
CRUD Operation Smart Merging
Conflict Prevention:
- Multiple updates to the same note are merged into single operations
- UPDATE operations on locally created notes are merged into CREATE operations
- Prevents server errors from attempting to update non-existent entities
Operation Dependencies:
- Attachment operations are properly sequenced with note operations
- Dependency tracking ensures operations execute in correct order
- Prevents data consistency issues during sync
Sync Performance Features
Delta Sync Optimization:
// Background delta sync only fetches notes updated since last sync
// Uses efficient API filtering: updatedAfter = lastSyncTimestamp
// Minimizes data transfer and processing time
Transaction Optimization:
- Database operations use atomic transactions for consistency
- Bulk operations are batched to reduce transaction overhead
- Rollback protection ensures data integrity on failures
Memory Management:
- Lazy attachment loading prevents memory bloat
- Efficient database cursors for large result sets
- Smart caching with automatic cleanup
Smart Relationship Handling
Attachment Association:
// SDK automatically handles complex attachment relationships
val noteWithAttachments = ZyncAddNoteContent(
note = "Equipment inspection report",
attachments = listOf(
// Mix of existing attachments and new file paths
ZyncNoteAttachment(
attachmentUid = "existing-attachment-uid",
attachment = "file:///path/to/existing/photo.jpg",
attachmentName = "existing_photo.jpg",
attachmentType = "IMAGE",
attachmentSize = 1536000L,
attachmentDescription = "Existing attachment",
tags = listOf("existing"),
thumbnailUrl = null
),
ZyncNoteAttachment(
attachmentUid = "new-attachment-uid",
attachment = "file:///path/to/new/photo.jpg",
attachmentName = "new_photo.jpg",
attachmentType = "IMAGE",
attachmentSize = 2048000L,
attachmentDescription = "New attachment",
tags = listOf("new"),
thumbnailUrl = null
)
),
// ... other properties
)
// Behind the scenes:
// - Validates existing attachments exist
// - Creates new attachments from file paths
// - Establishes proper note-attachment relationships
// - Handles CRUD operation dependencies automatically
User Mention Processing:
- User mentions are validated and processed efficiently
- Associated user data is cached to prevent repeated lookups
- Cleanup of invalid or deleted user references
Performance Monitoring
The SDK automatically tracks performance metrics:
- Sync Duration: Time taken for initial and delta syncs
- Operation Counts: Number of notes processed per sync
- Error Rates: Failed operations with automatic retry logic
- Cache Hit Rates: Efficiency of local cache usage
Best Performance: The SDK is optimized for typical usage patterns. For best performance, let the SDK handle sync timing automatically rather than forcing manual refreshes.
Best Practices
When to Use Each Method
Use getNotes() for:
- Loading note lists for UI display
- Regular data access patterns
- Letting the SDK handle sync automatically
Use getNoteDetail() for:
- Accessing individual note content
- Displaying note details
- Cached note retrieval
Use markNoteAsStale() when:
- You’ve made server-side changes outside the SDK
- You suspect local cache is outdated
- After receiving push notifications about note updates
- Following online-only visibility changes
Use addNote() and updateNote() for:
- All local note creation and modification
- Offline-first user interactions
- Batch operations that should sync when online
Optimal Usage Patterns
Efficient Note Loading
class NotesViewModel {
private var cachedNotes: List<ZyncNote> = emptyList()
private var isInitialLoad = true
suspend fun loadNotes() {
val result = zync.notes.getNotes(
module = ZuperModule.JOB,
moduleUid = jobUid
)
when (result) {
is NotesResult.Success -> {
cachedNotes = result.notes
// First load takes longer (initial sync)
// Subsequent loads are instant (cached + background sync)
isInitialLoad = false
updateUI(result.notes)
}
is NotesResult.Failure -> {
handleError(result.error)
}
}
}
// Only use markNoteAsStale when you know data might be outdated
suspend fun refreshSpecificNote(noteUid: String) {
zync.notes.markNoteAsStale(noteUid)
// Reload notes to get updated data
loadNotes()
}
}
Offline-First Operations
// Create notes offline - they'll sync automatically
suspend fun createInspectionNote(jobUid: String, photos: List<String>) {
val noteContent = ZyncAddNoteContent(
note = "Equipment inspection completed",
noteType = "TEXT",
canNotifyOthers = true,
noteVisibilityType = ZyncNoteVisibilityType.PUBLIC,
attachments = photos.mapIndexed { index, photoPath ->
ZyncNoteAttachment(
attachmentUid = "photo-${System.currentTimeMillis()}-$index",
attachment = photoPath,
attachmentName = "photo_$index.jpg",
attachmentType = "IMAGE",
attachmentSize = 2048576L,
attachmentDescription = "Inspection photo $index",
tags = listOf("inspection", "equipment"),
thumbnailUrl = null
)
},
primaryModule = ZyncNotePrimaryModule("job", jobUid),
isPinned = false,
geoCoordinates = getCurrentLocation()
)
val result = zync.notes.addNote(content = noteContent)
// Note 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
- Create and update notes optimistically (offline-first)
- Batch attachment operations with note operations
- Use background sync for keeping data fresh
- Use
updatedAtto track note modifications and sort by recency
Don’ts ❌
- Don’t call
markNoteAsStale()frequently or on every load - Don’t force manual sync unless absolutely necessary
- Don’t ignore offline capabilities - embrace offline-first patterns
- Don’t create separate attachment operations - include them with notes
Error Handling Best Practices
suspend fun handleNoteOperations() {
try {
val result = zync.notes.getNotes(module, moduleUid)
when (result) {
is NotesResult.Success -> {
// Handle success
displayNotes(result.notes)
}
is NotesResult.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 notes. Using cached data.")
loadCachedData()
}
}
}
}
} catch (exception: Exception) {
// Handle unexpected errors gracefully
logError(exception)
showGenericErrorMessage()
}
}
Memory and Resource Management
- Lazy Loading: The SDK loads attachments lazily to conserve memory
- Automatic Cleanup: Database cleanup happens automatically during sync
- Resource Optimization: File attachments are managed efficiently with deduplication
Security Considerations
- Offline Security: Notes are stored securely in encrypted local database
- Sync Security: All network operations use secure connections
- Data Privacy: User mentions and sensitive 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.