Skip to content

Commit a1e18b3

Browse files
committed
fix: iOS threading issues
1 parent 554bbfe commit a1e18b3

File tree

6 files changed

+166
-69
lines changed

6 files changed

+166
-69
lines changed

android/src/main/java/com/margelo/nitro/fs2/Fs2.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ class Fs2() : HybridFs2Spec() {
397397
)
398398
)
399399

400-
downloadPromise.reject(throw reject(options.toFile, e)) // Also rethrow for the promise rejection
400+
downloadPromise.reject(reject(options.toFile, e)) // Also rethrow for the promise rejection
401401
}
402402

403403
return downloadPromise

android/src/main/java/com/margelo/nitro/fs2/MediaStore.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package com.margelo.nitro.fs2
22

3-
import com.margelo.nitro.NitroModules
43
import com.margelo.nitro.core.Promise
54
import androidx.core.net.toUri
65

76
class MediaStore(): HybridMediaStoreSpec() {
8-
private val reactContext = NitroModules.applicationContext!!
9-
private val mediaStoreManager = RNFSMediaStoreManager(reactContext)
7+
private val mediaStoreManager = RNFSMediaStoreManager()
108

119
private fun reject(context: String, ex: Exception): Throwable {
1210
// You can expand this for more specific error types as needed
@@ -77,16 +75,22 @@ class MediaStore(): HybridMediaStoreSpec() {
7775
}
7876

7977
override fun mediaStoreQueryFile(searchOptions: MediaStoreSearchOptions): Promise<MediaStoreFile?> {
80-
return Promise.async {
81-
try {
82-
val file = mediaStoreManager.query(searchOptions)
83-
println("MediaStore query result: $file")
78+
val queryPromise:Promise<MediaStoreFile?> = Promise()
8479

85-
return@async file
86-
} catch (e: Exception) {
87-
throw reject(searchOptions.fileName ?: "query", e)
80+
try {
81+
val file = mediaStoreManager.query(searchOptions)
82+
83+
if (file != null) {
84+
queryPromise.resolve(file)
85+
} else {
86+
println("File not found: ${searchOptions.fileName}")
87+
queryPromise.reject(Error("File not found: ${searchOptions.fileName}"))
8888
}
89+
} catch (e: Exception) {
90+
throw reject(searchOptions.fileName ?: "query", e)
8991
}
92+
93+
return queryPromise
9094
}
9195

9296
override fun mediaStoreDeleteFile(uri: String): Promise<Boolean> {

android/src/main/java/com/margelo/nitro/fs2/RNFSMediaStoreManager.kt

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ import java.io.FileOutputStream
1717
import java.io.IOException
1818
import java.io.OutputStream
1919

20-
class RNFSMediaStoreManager(private val context: Context) {
20+
import com.margelo.nitro.NitroModules
21+
22+
class RNFSMediaStoreManager {
23+
private val context = NitroModules.applicationContext
24+
?: throw IllegalStateException("NitroModules.applicationContext is null")
25+
2126
companion object {
27+
private const val BUFFER_SIZE = 10240
2228
private fun getMediaUri(mt: MediaCollectionType): Uri? {
2329
return when (mt) {
2430
MediaCollectionType.AUDIO ->
@@ -111,35 +117,32 @@ class RNFSMediaStoreManager(private val context: Context) {
111117
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
112118
throw UnsupportedOperationException("Android version not supported")
113119
val resolver = context.contentResolver
114-
var stream: OutputStream? = null
115120
try {
116121
val src = File(filePath)
117122
if (!src.exists()) throw IOException("No such file ('$filePath')")
118123
val descr =
119124
resolver.openFileDescriptor(fileUri, "w")
120125
?: throw IOException("Failed to open file descriptor")
126+
121127
FileInputStream(src).use { fin ->
122128
FileOutputStream(descr.fileDescriptor).use { out ->
123129
if (transformFile) {
124130
val bytes = fin.readBytes()
125131
// Implement transformation logic if needed
126132
out.write(bytes) // No transformation in this version
127133
} else {
128-
val buf = ByteArray(1024 * 10)
134+
val buf = ByteArray(BUFFER_SIZE)
129135
var read: Int
130136
while (fin.read(buf).also { read = it } > 0) {
131137
out.write(buf, 0, read)
132138
}
133139
}
134140
}
135141
}
136-
stream = resolver.openOutputStream(fileUri)
137-
stream?.close()
142+
138143
return true
139144
} catch (e: Exception) {
140145
throw IOException("Failed to write file: ${e.message}", e)
141-
} finally {
142-
stream?.close()
143146
}
144147
}
145148

@@ -192,10 +195,16 @@ class RNFSMediaStoreManager(private val context: Context) {
192195
val queryFilename: String = query.fileName ?: ""
193196
val queryRelativePath: String = query.relativePath ?: ""
194197

198+
val mediaCollectionType = try {
199+
MediaCollectionType.valueOf(queryMediaType)
200+
} catch (e: IllegalArgumentException) {
201+
Log.e("RNFS2", "Invalid media type provided: $queryMediaType")
202+
return null
203+
}
195204

196205
val mediaURI =
197206
if (queryUri.isNotEmpty()) queryUri.toUri()
198-
else getMediaUri(MediaCollectionType.valueOf(queryMediaType))
207+
else getMediaUri(mediaCollectionType)
199208
val projection =
200209
arrayOf(
201210
MediaStore.MediaColumns._ID,
@@ -209,7 +218,7 @@ class RNFSMediaStoreManager(private val context: Context) {
209218
val selection: String?
210219
val selectionArgs: Array<String>?
211220
if (queryUri.isEmpty()) {
212-
val relativePath = getRelativePath(MediaCollectionType.valueOf(queryMediaType))
221+
val relativePath = getRelativePath(mediaCollectionType)
213222
selection =
214223
MediaStore.MediaColumns.DISPLAY_NAME +
215224
" = ? AND " +

example/src/example4.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import { getTestFolder, getFolderText } from './utils';
1313

1414
const EXAMPLE_FOLDER = 'RNFS2Example4Folder';
1515
const FILE_NAME = 'picsum.jpg';
16-
const FILE_URL = 'https://picsum.photos/200';
16+
const FILE_URL = 'https://picsum.photos/seed/picsum/200/300';
17+
18+
const FILE_NAME2 = 'picsum2.jpg';
19+
const FILE_URL2 = 'https://picsum.photos/id/237/200/300';
1720

1821
const Example = () => {
1922
const [runningAction, setRunningAction] = useState(false);
@@ -36,6 +39,7 @@ const Example = () => {
3639

3740
// Download file
3841
const filePath = `${folder}/${EXAMPLE_FOLDER}/${FILE_NAME}`;
42+
const filePath2 = `${folder}/${EXAMPLE_FOLDER}/${FILE_NAME2}`;
3943
runStatus += `\n- Downloading file from ${FILE_URL}`;
4044
setResult(runStatus);
4145

@@ -76,6 +80,27 @@ const Example = () => {
7680
console.log('downloadFile canBeResumed', event);
7781
},
7882
});
83+
84+
RNFS.downloadFile({
85+
fromUrl: FILE_URL2,
86+
toFile: filePath2,
87+
jobId,
88+
begin: (event) => {
89+
console.log('downloadFile begin', event);
90+
},
91+
progress: (event) => {
92+
console.log('downloadFile progress', event);
93+
},
94+
complete: async (event) => {
95+
console.log('downloadFile complete', event);
96+
},
97+
error: (event) => {
98+
console.log('downloadFile error', event);
99+
},
100+
canBeResumed: (event) => {
101+
console.log('downloadFile canBeResumed', event);
102+
},
103+
});
79104
} catch (err) {
80105
setResult('Error Running Example: ' + err);
81106
} finally {

ios/Downloader.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ protocol DownloaderDelegate: AnyObject {
88
func downloadDidComplete(jobId: Int, statusCode: Int, bytesWritten: Int64)
99
func downloadDidError(jobId: Int, error: Error)
1010
func downloadCanBeResumed(jobId: Int)
11+
func downloadCleanup(jobId: Int)
1112
}
1213

1314
class Downloader: NSObject, URLSessionDownloadDelegate {
@@ -30,6 +31,7 @@ class Downloader: NSObject, URLSessionDownloadDelegate {
3031
// MARK: - Public API
3132

3233
func startDownload(from url: URL, to destination: String, headers: [String: String]?, options: DownloadFileOptions?) {
34+
3335
if FileManager.default.fileExists(atPath: destination) {
3436
do {
3537
let fileHandle = try FileHandle(forWritingTo: URL(fileURLWithPath: destination))
@@ -191,6 +193,7 @@ class Downloader: NSObject, URLSessionDownloadDelegate {
191193
defer {
192194
self.session?.finishTasksAndInvalidate()
193195
self.clearState()
196+
delegate?.downloadCleanup(jobId: jobId)
194197
}
195198

196199
// Move file to destination
@@ -218,6 +221,7 @@ class Downloader: NSObject, URLSessionDownloadDelegate {
218221
defer {
219222
self.session?.finishTasksAndInvalidate()
220223
self.clearState()
224+
delegate?.downloadCleanup(jobId: jobId)
221225
}
222226

223227
if let error = error as NSError? {

0 commit comments

Comments
 (0)