Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 126 additions & 101 deletions apps/test-suite/tests/MediaLibraryNext.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/expo-file-system/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

### 💡 Others

- Improve read/write performance on Android by applying `withContext(Dispatchers.IO)` when possible. ([#46376](https://github.com/expo/expo/pull/46376) by [@wh201906](https://github.com/wh201906))

## 56.0.7 — 2026-05-20

### 🛠 Breaking changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import android.util.Base64
import expo.modules.filesystem.unifiedfile.JavaFile
import expo.modules.filesystem.unifiedfile.SAFDocumentFile
import expo.modules.kotlin.exception.Exceptions
import expo.modules.kotlin.jni.NativeArrayBuffer
import expo.modules.kotlin.services.FilePermissionService
import expo.modules.kotlin.typedarray.TypedArray
import java.io.FileOutputStream
import java.security.MessageDigest

Expand Down Expand Up @@ -84,15 +84,15 @@ class FileSystemFile(uri: Uri) : FileSystemPath(uri) {
}
}

fun write(content: TypedArray, append: Boolean = false) {
fun write(content: NativeArrayBuffer, append: Boolean = false) {
validateType()
validatePermission(FilePermissionService.Permission.WRITE)
if (!exists) {
create()
}
if (uri.isContentUri) {
file.outputStream(append).use { outputStream ->
val array = ByteArray(content.length)
val array = ByteArray(content.size())
content.toDirectBuffer().get(array)
outputStream.write(array)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import androidx.annotation.RequiresApi
import expo.modules.kotlin.activityresult.AppContextActivityResultLauncher
import expo.modules.kotlin.exception.Exceptions
import expo.modules.kotlin.functions.Coroutine
import expo.modules.kotlin.jni.NativeArrayBuffer
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
import expo.modules.kotlin.services.FilePermissionService
import expo.modules.kotlin.typedarray.TypedArray
import expo.modules.kotlin.types.Either
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File
import java.net.URI

Expand All @@ -31,7 +33,7 @@ class FileSystemModule : Module() {

private fun writeToFile(
file: FileSystemFile,
content: Either<String, TypedArray>,
content: Either<String, NativeArrayBuffer>,
options: WriteOptions?
) {
val append = options?.append ?: false
Expand All @@ -43,8 +45,8 @@ class FileSystemModule : Module() {
file.write(it, append)
}
}
} else if (content.`is`(TypedArray::class)) {
content.get(TypedArray::class).let {
} else if (content.`is`(NativeArrayBuffer::class)) {
content.get(NativeArrayBuffer::class).let {
file.write(it, append)
}
}
Expand Down Expand Up @@ -159,32 +161,40 @@ class FileSystemModule : Module() {
file.create(options ?: CreateOptions())
}

AsyncFunction("write") Coroutine { file: FileSystemFile, content: Either<String, TypedArray>, options: WriteOptions? ->
writeToFile(file, content, options)
AsyncFunction("write") Coroutine { file: FileSystemFile, content: Either<String, NativeArrayBuffer>, options: WriteOptions? ->
withContext(Dispatchers.IO) {
writeToFile(file, content, options)
}
}

Function("writeSync") { file: FileSystemFile, content: Either<String, TypedArray>, options: WriteOptions? ->
Function("writeSync") { file: FileSystemFile, content: Either<String, NativeArrayBuffer>, options: WriteOptions? ->
writeToFile(file, content, options)
}

AsyncFunction("text") { file: FileSystemFile ->
file.text()
AsyncFunction("text") Coroutine { file: FileSystemFile ->
withContext(Dispatchers.IO) {
file.text()
}
}

Function("textSync") { file: FileSystemFile ->
file.text()
}

AsyncFunction("base64") { file: FileSystemFile ->
file.base64()
AsyncFunction("base64") Coroutine { file: FileSystemFile ->
withContext(Dispatchers.IO) {
file.base64()
}
}

Function("base64Sync") { file: FileSystemFile ->
file.base64()
}

AsyncFunction("bytes") { file: FileSystemFile ->
file.bytes()
AsyncFunction("bytes") Coroutine { file: FileSystemFile ->
withContext(Dispatchers.IO) {
file.bytes()
}
}

Function("bytesSync") { file: FileSystemFile ->
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions packages/expo-file-system/ios/FileSystemFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,11 @@ internal final class FileSystemFile: FileSystemPath {
}

// TODO: blob support
func write(_ content: TypedArray, append: Bool = false) throws {
func write(_ content: NativeArrayBuffer, append: Bool = false) throws {
try withCorrectTypeAndScopedAccess(permission: .write) {
let data = Data(bytes: content.rawPointer, count: content.byteLength)
let data = content.withUnsafeBytes { rawBuffer in
Data(rawBuffer)
}
if append {
try writeAppending(data)
} else {
Expand Down
8 changes: 4 additions & 4 deletions packages/expo-file-system/ios/FileSystemModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public final class FileSystemModule: Module {

private func writeToFile(
_ file: FileSystemFile,
content: Either<String, TypedArray>,
content: Either<String, NativeArrayBuffer>,
options: WriteOptions?
) throws {
let append = options?.append ?? false
Expand All @@ -49,7 +49,7 @@ public final class FileSystemModule: Module {
} else {
try file.write(content, append: append)
}
} else if let content: TypedArray = content.get() {
} else if let content: NativeArrayBuffer = content.get() {
try file.write(content, append: append)
}
}
Expand Down Expand Up @@ -194,11 +194,11 @@ public final class FileSystemModule: Module {
return try file.info(options: options ?? InfoOptions())
}

AsyncFunction("write") { (file: FileSystemFile, content: Either<String, TypedArray>, options: WriteOptions?) in
AsyncFunction("write") { (file: FileSystemFile, content: Either<String, NativeArrayBuffer>, options: WriteOptions?) in
try writeToFile(file, content: content, options: options)
}

Function("writeSync") { (file: FileSystemFile, content: Either<String, TypedArray>, options: WriteOptions?) in
Function("writeSync") { (file: FileSystemFile, content: Either<String, NativeArrayBuffer>, options: WriteOptions?) in
try writeToFile(file, content: content, options: options)
}

Expand Down
4 changes: 2 additions & 2 deletions packages/expo-file-system/mocks/FileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ export class FileSystemFile {
}

writeSync(
content: string | Uint8Array,
content: string | Uint8Array | ArrayBuffer,
options: { append?: boolean; encoding?: 'utf8' | 'base64' } = {}
): void {
assertParent(this.uri, false);
Expand Down Expand Up @@ -281,7 +281,7 @@ export class FileSystemFile {
}

async write(
content: string | Uint8Array,
content: string | Uint8Array | ArrayBuffer,
options: { append?: boolean; encoding?: 'utf8' | 'base64' } = {}
): Promise<void> {
this.writeSync(content, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ describe('expo-file-system behavioral mock', () => {
await expect(file.bytes()).resolves.toEqual(payload);
});

it('File.writeSync(ArrayBuffer) and File.bytes() roundtrip byte-for-byte', async () => {
const file = new File(Paths.cache, 'bin-buffer.dat');
const payload = Uint8Array.from([6, 7, 8, 9]).buffer;
file.writeSync(payload);
expect(Array.from(file.bytesSync())).toEqual([6, 7, 8, 9]);
await expect(file.bytes()).resolves.toEqual(new Uint8Array(payload));
});

it('File.writeSync with append option appends to existing bytes', () => {
const file = new File(Paths.cache, 'log.txt');
file.writeSync('a');
Expand Down
Loading
Loading