Task model
Every SMB operation returns SmbTask<T>. Creating the task starts native work
immediately — you choose when to subscribe, await, or cancel.
Overview
Three optional actions on any task:
| Action | When | API |
|---|---|---|
| Get result | You need the return value | await task.result() |
| Watch progress | UI needs live updates | task.subscribe() or useSubscribe |
| Cancel | User aborts | task.cancel() |
Get the result
await smb.connect('smb://192.168.1.100/Share', creds).result(); const files = await smb.listDirectory('/', false, -1).result(); await smb.downloadFile('/doc.pdf', '/local/doc.pdf').result();
Hold the handle and await later:
const task = smb.copyItem('/src', '/dst', true); await task.result();
Batch multiple tasks — like Promise.allSettled:
import { SmbTask } from '@cetapod/react-native-smb'; const tasks = files.map((f) => smb.deleteItem(f.path)); const results = await SmbTask.settleAll(tasks);
Watch progress
const task = smb.downloadFile(remote, local); // Live getters on the task object const unsub = task.subscribe(); // Or with a callback const unsub2 = task.subscribe((snap) => { console.log(snap.progress, snap.bytesPerSecond, snap.status); }); // Read getters after subscribing console.log(task.progress, task.status);
React — useSubscribe subscribes automatically on mount (and whenever task changes), and unsubscribes on unmount:
import { useSubscribe, statusLabel } from '@cetapod/react-native-smb'; function DownloadBar({ task }) { const { progress, status, cancel } = useSubscribe(task); return ( <> <Text>{Math.round(progress * 100)}%</Text> <Text>{statusLabel(status)}</Text> <Button onPress={cancel} title="Cancel" /> </> ); }
The hook also returns subscribe, in case you want to attach an extra listener
callback (e.g. for logging) on top of the automatic subscription:
const { subscribe } = useSubscribe(task); useEffect(() => subscribe((snap) => console.log(snap)), [subscribe]);
task.progress and task.status are live getters on the imperative API — read
them after calling task.subscribe(). useSubscribe subscribes for you
automatically, so its progress/status are always live.
Snapshot fields
subscribe() and get() return SmbTaskState:
| Field | Description |
|---|---|
progress | 0–1 (determinate for transfers) |
status | TaskStatus enum |
bytesDone / totalBytes | Raw byte counts |
bytesPerSecond / etaSeconds | Throughput estimate |
determinate | true for download/upload/copy |
sourcePath / destinationPath | Paths for transfer ops |
errorCode / errorMessage | Set on failure |
Cancel
const task = smb.downloadFile(remote, local); task.subscribe(); // User taps cancel task.cancel(); unsub();
Cancelled tasks reject with SmbTaskError where code === SmbError.Cancelled.
Fire-and-forget
Start work without awaiting — useful for bulk ops with a transfer tray:
files.forEach((f) => smb.downloadFile(f.remote, f.local)); // Reload when all settle const tasks = files.map((f) => smb.deleteItem(f.path)); void SmbTask.settleAll(tasks).then(() => reload());
Transfer tray
import { useTransfers, useTransferActions } from '@cetapod/react-native-smb'; function TransferTray({ smb }) { const tasks = useTransfers(smb); const { clearCompleted, hide } = useTransferActions(smb); // tasks is SmbTaskState[] — all active transfers }
See the example app's TransferTrayPanel for a complete implementation.
Error handling
import { SmbError, SmbTaskError } from '@cetapod/react-native-smb'; try { await smb.getPathInfo('/nonexistent').result(); } catch (error) { if (error instanceof SmbTaskError) { if (error.code === SmbError.NotFound) { /* ... */ } if (error.code === SmbError.AccessDenied) { /* ... */ } } }
See SmbError for all error codes.
Lifecycle
When the user logs out or you're done with a client, call dispose():
smb.dispose(); // stops transfer store listener
Sync state checks (isConnected, isInitialized) are available anytime without a task.