core/file/README.md
This module provides a simple, consistent API for common file operations across Android and JVM platforms.
The file system layer is split into two levels:
FileSystemManager opens RawSource/RawSink for a given Uri.
AndroidFileSystemManagerJvmFileSystemManagerFileManager for common operations (copy, delete, createDirectories).
DefaultFileManager delegating to internal commandsCopyCommand(source, dest), DeleteCommand(uri), CreateDirectoriesCommand(dir) implement operations using FileSystemManager.
Outcome<Unit, FileOperationError> internally to preserve error context.RawSource/RawSink come from kotlinx-io and are referenced in the public API.classDiagram
class FileManager {
+copy(source: Uri, dest: Uri): Outcome<Unit, FileOperationError>
}
class DefaultFileManager {
-fs: FileSystemManager
}
class FileSystemManager {
+openSource(uri: Uri): RawSource?
+openSink(uri: Uri, mode: WriteMode = WriteMode.Truncate): RawSink?
}
class CopyCommand {
-source: Uri
-destination: Uri
+invoke(fs: FileSystemManager): Outcome<Unit, FileOperationError>
}
class FileOperationError {
}
DefaultFileManager ..> FileSystemManager
CopyCommand --> FileSystemManager
DefaultFileManager ..> CopyCommand : delegates
Add the module to your Gradle build. Then, depending on your platform, provide an actual FileSystemManager and wire a FileManager:
// Koin example (Android)
single<FileSystemManager> { AndroidFileSystemManager(androidContext().contentResolver) }
single<FileManager> { DefaultFileManager(get()) }
For JVM-only tools/tests:
val fs: FileSystemManager = JvmFileSystemManager()
val fileManager: FileManager = DefaultFileManager(fs)
suspend fun copy(sourceUri: Uri, destinationUri: Uri): Outcome<Unit, FileOperationError>suspend fun delete(uri: Uri): Outcome<Unit, FileOperationError>suspend fun createDirectories(uri: Uri): Outcome<Unit, FileOperationError>fun openSource(uri: Uri): RawSource?fun openSink(uri: Uri, mode: WriteMode = WriteMode.Truncate): RawSink?fun delete(uri: Uri)fun createDirectories(uri: Uri)WriteMode.Append to append where supported.enum class WriteMode { Truncate, Append }The API uses a KMP‑friendly Uri type (com.eygraber.uri.Uri). On Android, convert a platform URI using the provided extension:
val kmpUri = androidUri.toKmpUri()
To build URIs in tests or common code, you can parse a string:
val source = "file:///path/to/file.txt".toKmpUri()
content:// via ContentResolverfile:// via ContentResolverfile:// URIs only (non-file: schemes are not supported and will return null).NSFileManager/NSURL.openSource(uri)/openSink(uri) return null on failure. Always check for null and handle gracefully (e.g., show a message, request permissions).BUFFER_SIZE = 8_192L).openSource/openSink are not suspending; perform I/O on an appropriate dispatcher/thread when needed.takePersistableUriPermission).file: URIs are supported by JvmFileSystemManager.NSFileManager/NSURL.