react-native-nitro-fetch 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/margelo/nitro/nitrofetch/AutoPrefetcher.kt +7 -28
- package/android/src/main/java/com/margelo/nitro/nitrofetch/FetchCache.kt +11 -1
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NativeStorage.kt +102 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchClient.kt +53 -29
- package/ios/NativeStorage.swift +61 -0
- package/ios/NitroAutoPrefetcher.swift +5 -41
- package/ios/NitroFetchClient.swift +25 -0
- package/lib/module/NitroFetch.nitro.js +0 -3
- package/lib/module/NitroFetch.nitro.js.map +1 -1
- package/lib/module/NitroInstances.js +2 -0
- package/lib/module/NitroInstances.js.map +1 -1
- package/lib/module/fetch.js +79 -125
- package/lib/module/fetch.js.map +1 -1
- package/lib/typescript/src/NitroFetch.nitro.d.ts +9 -0
- package/lib/typescript/src/NitroFetch.nitro.d.ts.map +1 -1
- package/lib/typescript/src/NitroInstances.d.ts +3 -1
- package/lib/typescript/src/NitroInstances.d.ts.map +1 -1
- package/lib/typescript/src/fetch.d.ts.map +1 -1
- package/nitro.json +4 -0
- package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.cpp +54 -0
- package/nitrogen/generated/android/c++/JHybridNativeStorageSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.cpp +5 -0
- package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.hpp +1 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNativeStorageSpec.kt +60 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/HybridNitroFetchClientSpec.kt +4 -0
- package/nitrogen/generated/android/nitrofetch+autolinking.cmake +9 -4
- package/nitrogen/generated/android/nitrofetchOnLoad.cpp +10 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.hpp +44 -0
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Umbrella.hpp +5 -0
- package/nitrogen/generated/ios/NitroFetchAutolinking.mm +8 -0
- package/nitrogen/generated/ios/NitroFetchAutolinking.swift +15 -0
- package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridNativeStorageSpecSwift.hpp +85 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.hpp +8 -0
- package/nitrogen/generated/ios/swift/HybridNativeStorageSpec.swift +51 -0
- package/nitrogen/generated/ios/swift/HybridNativeStorageSpec_cxx.swift +145 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridNitroFetchClientSpec_cxx.swift +12 -0
- package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.cpp +23 -0
- package/nitrogen/generated/shared/c++/HybridNativeStorageSpec.hpp +64 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridNitroFetchClientSpec.hpp +1 -0
- package/package.json +8 -24
- package/src/NitroFetch.nitro.ts +15 -5
- package/src/NitroInstances.ts +7 -1
- package/src/fetch.ts +158 -122
- package/LICENSE +0 -20
- package/README.md +0 -151
package/android/build.gradle
CHANGED
|
@@ -117,11 +117,11 @@ dependencies {
|
|
|
117
117
|
implementation "com.facebook.react:react-android"
|
|
118
118
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
119
119
|
implementation project(":react-native-nitro-modules")
|
|
120
|
-
implementation("com.tencent:mmkv:1.3.14")// or latest
|
|
121
120
|
// Provide org.chromium.net Java API for CronetEngine in Kotlin
|
|
122
121
|
// Cronet
|
|
123
122
|
api "org.chromium.net:cronet-embedded:${cronetVersion}"
|
|
124
123
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
|
|
124
|
+
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
configurations {
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
package com.margelo.nitro.nitrofetch
|
|
2
2
|
|
|
3
3
|
import android.app.Application
|
|
4
|
+
import android.content.Context
|
|
4
5
|
import org.json.JSONArray
|
|
5
6
|
import org.json.JSONObject
|
|
6
7
|
import java.util.concurrent.CompletableFuture
|
|
7
|
-
import com.tencent.mmkv.MMKV;
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
object AutoPrefetcher {
|
|
11
11
|
@Volatile private var initialized = false
|
|
12
|
+
private const val KEY_QUEUE = "nitrofetch_autoprefetch_queue"
|
|
13
|
+
private const val PREFS_NAME = "nitro_fetch_storage"
|
|
12
14
|
|
|
13
15
|
fun prefetchOnStart(app: Application) {
|
|
14
16
|
if (initialized) return
|
|
15
17
|
initialized = true
|
|
16
18
|
try {
|
|
17
|
-
val
|
|
18
|
-
val raw =
|
|
19
|
+
val prefs = app.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
20
|
+
val raw = prefs.getString(KEY_QUEUE, null) ?: ""
|
|
19
21
|
if (raw.isEmpty()) return
|
|
20
22
|
val arr = JSONArray(raw)
|
|
21
23
|
for (i in 0 until arr.length()) {
|
|
@@ -42,12 +44,8 @@ object AutoPrefetcher {
|
|
|
42
44
|
)
|
|
43
45
|
|
|
44
46
|
// If already pending or fresh, skip starting a new one
|
|
45
|
-
if (FetchCache.getPending(prefetchKey) != null)
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
if (FetchCache.getResultIfFresh(prefetchKey, 5_000L) != null) {
|
|
49
|
-
continue
|
|
50
|
-
}
|
|
47
|
+
if (FetchCache.getPending(prefetchKey) != null) continue
|
|
48
|
+
if (FetchCache.hasFreshResult(prefetchKey, 5_000L)) continue
|
|
51
49
|
|
|
52
50
|
val future = CompletableFuture<NitroResponse>()
|
|
53
51
|
FetchCache.setPending(prefetchKey, future)
|
|
@@ -71,23 +69,4 @@ object AutoPrefetcher {
|
|
|
71
69
|
// ignore – prefetch-on-start is best-effort
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
|
-
|
|
75
|
-
private const val KEY_QUEUE = "nitrofetch_autoprefetch_queue"
|
|
76
|
-
|
|
77
|
-
private fun getMMKV(app: Application): Any? {
|
|
78
|
-
return try {
|
|
79
|
-
MMKV.initialize(app);
|
|
80
|
-
|
|
81
|
-
return MMKV.defaultMMKV()
|
|
82
|
-
} catch (_: Throwable) {
|
|
83
|
-
null
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
private fun invokeMMKVDecodeString(mmkv: Any, key: String): String? {
|
|
88
|
-
return try {
|
|
89
|
-
val m = mmkv.javaClass.getMethod("decodeString", String::class.java, String::class.java)
|
|
90
|
-
m.invoke(mmkv, key, null) as? String
|
|
91
|
-
} catch (_: Throwable) { null }
|
|
92
|
-
}
|
|
93
72
|
}
|
|
@@ -41,8 +41,18 @@ object FetchCache {
|
|
|
41
41
|
return if (age <= maxAgeMs) entry.response else null
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Check if a fresh result exists WITHOUT consuming it.
|
|
46
|
+
* Used to check if we should skip starting a new prefetch.
|
|
47
|
+
*/
|
|
48
|
+
fun hasFreshResult(key: String, maxAgeMs: Long): Boolean {
|
|
49
|
+
val entry = results[key] ?: return false
|
|
50
|
+
val age = System.currentTimeMillis() - entry.timestampMs
|
|
51
|
+
return age <= maxAgeMs
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
fun clear() {
|
|
45
55
|
pending.clear()
|
|
46
56
|
results.clear()
|
|
47
57
|
}
|
|
48
|
-
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
package com.margelo.nitro.nitrofetch
|
|
2
|
+
|
|
3
|
+
import android.app.Application
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.SharedPreferences
|
|
6
|
+
import android.util.Log
|
|
7
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
8
|
+
import com.margelo.nitro.NitroModules
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@DoNotStrip
|
|
13
|
+
class NativeStorage : HybridNativeStorageSpec() {
|
|
14
|
+
|
|
15
|
+
companion object {
|
|
16
|
+
private const val TAG = "HybridNativeStorage"
|
|
17
|
+
private const val PREFS_NAME = "nitro_fetch_storage"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
private val sharedPreferences: SharedPreferences by lazy {
|
|
21
|
+
val context = NitroModules.applicationContext ?: throw Error("Cannot get Android Context - No Context available!")
|
|
22
|
+
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Retrieves a string value for the given key.
|
|
30
|
+
*
|
|
31
|
+
* @param key The key to look up in storage
|
|
32
|
+
* @return The stored string value, or empty string if key doesn't exist
|
|
33
|
+
* @throws IllegalStateException if SharedPreferences is not available
|
|
34
|
+
*/
|
|
35
|
+
override fun getString(key: String): String {
|
|
36
|
+
return try {
|
|
37
|
+
val value = sharedPreferences.getString(key, null)
|
|
38
|
+
if (value != null) {
|
|
39
|
+
Log.d(TAG, "Retrieved value for key: $key")
|
|
40
|
+
value
|
|
41
|
+
} else {
|
|
42
|
+
Log.d(TAG, "Key not found: $key, returning empty string")
|
|
43
|
+
""
|
|
44
|
+
}
|
|
45
|
+
} catch (t: Throwable) {
|
|
46
|
+
Log.e(TAG, "Error getting string for key: $key", t)
|
|
47
|
+
throw RuntimeException("Failed to get string for key: $key", t)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Stores a string value with the given key.
|
|
53
|
+
*
|
|
54
|
+
* @param key The key to store the value under
|
|
55
|
+
* @param value The string value to store
|
|
56
|
+
* @throws IllegalStateException if SharedPreferences is not available
|
|
57
|
+
* @throws RuntimeException if the write operation fails
|
|
58
|
+
*/
|
|
59
|
+
override fun setString(key: String, value: String) {
|
|
60
|
+
try {
|
|
61
|
+
val editor = sharedPreferences.edit()
|
|
62
|
+
editor.putString(key, value)
|
|
63
|
+
val success = editor.commit() // commit() is synchronous and returns boolean
|
|
64
|
+
if (success) {
|
|
65
|
+
Log.d(TAG, "Successfully stored value for key: $key")
|
|
66
|
+
} else {
|
|
67
|
+
Log.e(TAG, "Failed to commit value for key: $key")
|
|
68
|
+
throw RuntimeException("Failed to store value for key: $key")
|
|
69
|
+
}
|
|
70
|
+
} catch (t: Throwable) {
|
|
71
|
+
Log.e(TAG, "Error setting string for key: $key", t)
|
|
72
|
+
throw RuntimeException("Failed to set string for key: $key", t)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Deletes the value associated with the given key.
|
|
78
|
+
* If the key doesn't exist, this is a no-op.
|
|
79
|
+
*
|
|
80
|
+
* @param key The key to delete from storage
|
|
81
|
+
* @throws IllegalStateException if SharedPreferences is not available
|
|
82
|
+
* @throws RuntimeException if the delete operation fails
|
|
83
|
+
*/
|
|
84
|
+
override fun removeString(key: String) {
|
|
85
|
+
try {
|
|
86
|
+
val editor = sharedPreferences.edit()
|
|
87
|
+
editor.remove(key)
|
|
88
|
+
val success = editor.commit() // commit() is synchronous and returns boolean
|
|
89
|
+
if (success) {
|
|
90
|
+
Log.d(TAG, "Successfully deleted key: $key")
|
|
91
|
+
} else {
|
|
92
|
+
Log.e(TAG, "Failed to commit deletion for key: $key")
|
|
93
|
+
throw RuntimeException("Failed to delete key: $key")
|
|
94
|
+
}
|
|
95
|
+
} catch (t: Throwable) {
|
|
96
|
+
Log.e(TAG, "Error deleting key: $key", t)
|
|
97
|
+
throw RuntimeException("Failed to delete key: $key", t)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
@@ -155,6 +155,56 @@ class NitroFetchClient(private val engine: CronetEngine, private val executor: E
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
// Helper function to add prefetch header to response (reused by both sync/async)
|
|
159
|
+
private fun withPrefetchedHeader(res: NitroResponse): NitroResponse {
|
|
160
|
+
val newHeaders = (res.headers?.toMutableList() ?: mutableListOf())
|
|
161
|
+
newHeaders.add(NitroHeader("nitroPrefetched", "true"))
|
|
162
|
+
return NitroResponse(
|
|
163
|
+
url = res.url,
|
|
164
|
+
status = res.status,
|
|
165
|
+
statusText = res.statusText,
|
|
166
|
+
ok = res.ok,
|
|
167
|
+
redirected = res.redirected,
|
|
168
|
+
headers = newHeaders.toTypedArray(),
|
|
169
|
+
bodyString = res.bodyString,
|
|
170
|
+
bodyBytes = res.bodyBytes
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
override fun requestSync(req: NitroRequest): NitroResponse {
|
|
175
|
+
val key = findPrefetchKey(req)
|
|
176
|
+
if (key != null) {
|
|
177
|
+
FetchCache.getPending(key)?.let { fut ->
|
|
178
|
+
return try {
|
|
179
|
+
withPrefetchedHeader(fut.get()) // blocks until complete
|
|
180
|
+
} catch (e: Exception) {
|
|
181
|
+
throw e.cause ?: e
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
FetchCache.getResultIfFresh(key, 5_000L)?.let { cached ->
|
|
185
|
+
return withPrefetchedHeader(cached)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
val latch = java.util.concurrent.CountDownLatch(1)
|
|
189
|
+
var result: NitroResponse? = null
|
|
190
|
+
var error: Throwable? = null
|
|
191
|
+
|
|
192
|
+
fetch(
|
|
193
|
+
req,
|
|
194
|
+
onSuccess = {
|
|
195
|
+
result = it
|
|
196
|
+
latch.countDown()
|
|
197
|
+
},
|
|
198
|
+
onFail = {
|
|
199
|
+
error = it
|
|
200
|
+
latch.countDown()
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
latch.await()
|
|
204
|
+
error?.let { throw it }
|
|
205
|
+
return result!!
|
|
206
|
+
}
|
|
207
|
+
|
|
158
208
|
override fun request(req: NitroRequest): Promise<NitroResponse> {
|
|
159
209
|
val promise = Promise<NitroResponse>()
|
|
160
210
|
// Try to serve from prefetch cache/pending first
|
|
@@ -162,20 +212,6 @@ class NitroFetchClient(private val engine: CronetEngine, private val executor: E
|
|
|
162
212
|
if (key != null) {
|
|
163
213
|
// If a prefetch is currently pending, wait for it
|
|
164
214
|
FetchCache.getPending(key)?.let { fut ->
|
|
165
|
-
fun withPrefetchedHeader(res: NitroResponse): NitroResponse {
|
|
166
|
-
val newHeaders = (res.headers?.toMutableList() ?: mutableListOf())
|
|
167
|
-
newHeaders.add(NitroHeader("nitroPrefetched", "true"))
|
|
168
|
-
return NitroResponse(
|
|
169
|
-
url = res.url,
|
|
170
|
-
status = res.status,
|
|
171
|
-
statusText = res.statusText,
|
|
172
|
-
ok = res.ok,
|
|
173
|
-
redirected = res.redirected,
|
|
174
|
-
headers = newHeaders.toTypedArray(),
|
|
175
|
-
bodyString = res.bodyString,
|
|
176
|
-
bodyBytes = res.bodyBytes
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
215
|
fut.whenComplete { res, err ->
|
|
180
216
|
if (err != null) {
|
|
181
217
|
promise.reject(err)
|
|
@@ -189,19 +225,7 @@ class NitroFetchClient(private val engine: CronetEngine, private val executor: E
|
|
|
189
225
|
}
|
|
190
226
|
// If a fresh prefetched result exists (<=5s old), return it immediately
|
|
191
227
|
FetchCache.getResultIfFresh(key, 5_000L)?.let { cached ->
|
|
192
|
-
|
|
193
|
-
newHeaders.add(NitroHeader("nitroPrefetched", "true"))
|
|
194
|
-
val wrapped = NitroResponse(
|
|
195
|
-
url = cached.url,
|
|
196
|
-
status = cached.status,
|
|
197
|
-
statusText = cached.statusText,
|
|
198
|
-
ok = cached.ok,
|
|
199
|
-
redirected = cached.redirected,
|
|
200
|
-
headers = newHeaders.toTypedArray(),
|
|
201
|
-
bodyString = cached.bodyString,
|
|
202
|
-
bodyBytes = cached.bodyBytes
|
|
203
|
-
)
|
|
204
|
-
promise.resolve(wrapped)
|
|
228
|
+
promise.resolve(withPrefetchedHeader(cached))
|
|
205
229
|
return promise
|
|
206
230
|
}
|
|
207
231
|
}
|
|
@@ -220,8 +244,8 @@ class NitroFetchClient(private val engine: CronetEngine, private val executor: E
|
|
|
220
244
|
promise.reject(IllegalArgumentException("prefetch: missing 'prefetchKey' header"))
|
|
221
245
|
return promise
|
|
222
246
|
}
|
|
223
|
-
// If already have a fresh result, resolve immediately
|
|
224
|
-
FetchCache.
|
|
247
|
+
// If already have a fresh result, resolve immediately (NON-DESTRUCTIVE CHECK)
|
|
248
|
+
if (FetchCache.hasFreshResult(key, 5_000L)) {
|
|
225
249
|
promise.resolve(Unit)
|
|
226
250
|
return promise
|
|
227
251
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//
|
|
2
|
+
// NativeStorage.swift
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Ritesh Shukla on 08/11/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
final class NativeStorage: HybridNativeStorageSpec {
|
|
12
|
+
|
|
13
|
+
private static let suiteName = "nitro_fetch_storage"
|
|
14
|
+
|
|
15
|
+
private let userDefaults: UserDefaults
|
|
16
|
+
|
|
17
|
+
public override init() {
|
|
18
|
+
// Use a named suite for better isolation, fallback to standard if creation fails
|
|
19
|
+
if let suite = UserDefaults(suiteName: NativeStorage.suiteName) {
|
|
20
|
+
self.userDefaults = suite
|
|
21
|
+
} else {
|
|
22
|
+
self.userDefaults = UserDefaults.standard
|
|
23
|
+
}
|
|
24
|
+
super.init()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Retrieves a string value for the given key.
|
|
28
|
+
///
|
|
29
|
+
/// - Parameter key: The key to look up in storage
|
|
30
|
+
/// - Returns: The stored string value, or empty string if key doesn't exist
|
|
31
|
+
/// - Throws: RuntimeError if the operation fails
|
|
32
|
+
func getString(key: String) throws -> String {
|
|
33
|
+
guard let value = userDefaults.string(forKey: key) else {
|
|
34
|
+
return ""
|
|
35
|
+
}
|
|
36
|
+
return value
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Stores a string value with the given key.
|
|
40
|
+
///
|
|
41
|
+
/// - Parameters:
|
|
42
|
+
/// - key: The key to store the value under
|
|
43
|
+
/// - value: The string value to store
|
|
44
|
+
/// - Throws: RuntimeError if the write operation fails
|
|
45
|
+
func setString(key: String, value: String) throws {
|
|
46
|
+
userDefaults.set(value, forKey: key)
|
|
47
|
+
// Synchronize to ensure immediate persistence
|
|
48
|
+
userDefaults.synchronize()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Deletes the value associated with the given key.
|
|
52
|
+
/// If the key doesn't exist, this is a no-op.
|
|
53
|
+
///
|
|
54
|
+
/// - Parameter key: The key to delete from storage
|
|
55
|
+
/// - Throws: RuntimeError if the delete operation fails
|
|
56
|
+
func removeString(key: String) throws {
|
|
57
|
+
userDefaults.removeObject(forKey: key)
|
|
58
|
+
// Synchronize to ensure immediate persistence
|
|
59
|
+
userDefaults.synchronize()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -4,12 +4,16 @@ import Foundation
|
|
|
4
4
|
public final class NitroAutoPrefetcher: NSObject {
|
|
5
5
|
private static var initialized = false
|
|
6
6
|
private static let queueKey = "nitrofetch_autoprefetch_queue"
|
|
7
|
+
private static let suiteName = "nitro_fetch_storage"
|
|
7
8
|
|
|
8
9
|
@objc
|
|
9
10
|
public static func prefetchOnStart() {
|
|
10
11
|
if initialized { return }
|
|
11
12
|
initialized = true
|
|
12
|
-
|
|
13
|
+
|
|
14
|
+
// Read from UserDefaults
|
|
15
|
+
let userDefaults = UserDefaults(suiteName: suiteName) ?? UserDefaults.standard
|
|
16
|
+
guard let raw = userDefaults.string(forKey: queueKey), !raw.isEmpty else { return }
|
|
13
17
|
guard let data = raw.data(using: .utf8) else { return }
|
|
14
18
|
guard let arr = try? JSONSerialization.jsonObject(with: data, options: []) as? [Any] else { return }
|
|
15
19
|
|
|
@@ -32,46 +36,6 @@ public final class NitroAutoPrefetcher: NSObject {
|
|
|
32
36
|
}
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
|
-
|
|
36
|
-
// MARK: - MMKV dynamic access (optional)
|
|
37
|
-
|
|
38
|
-
private static func readMMKVString(forKey key: String) -> String? {
|
|
39
|
-
guard let mmkvClass = NSClassFromString("MMKV") as? NSObject.Type else { return nil }
|
|
40
|
-
// Try to initialize if needed (ignore failures)
|
|
41
|
-
let initSelectors = [
|
|
42
|
-
NSSelectorFromString("initializeMMKV:"),
|
|
43
|
-
NSSelectorFromString("initialize:")
|
|
44
|
-
]
|
|
45
|
-
for sel in initSelectors where mmkvClass.responds(to: sel) {
|
|
46
|
-
_ = mmkvClass.perform(sel, with: nil)
|
|
47
|
-
break
|
|
48
|
-
}
|
|
49
|
-
guard let mmkvObjUnretained = mmkvClass.perform(NSSelectorFromString("defaultMMKV"))?.takeUnretainedValue() else { return nil }
|
|
50
|
-
let mmkv = mmkvObjUnretained as AnyObject
|
|
51
|
-
// Try common selectors
|
|
52
|
-
let candidates = [
|
|
53
|
-
NSSelectorFromString("decodeStringForKey:"),
|
|
54
|
-
NSSelectorFromString("stringForKey:"),
|
|
55
|
-
NSSelectorFromString("getStringForKey:"),
|
|
56
|
-
]
|
|
57
|
-
for sel in candidates where mmkv.responds(to: sel) {
|
|
58
|
-
if let val = mmkv.perform(sel, with: key)?.takeUnretainedValue() as? String {
|
|
59
|
-
return val
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
// Some APIs have (forKey: defaultValue:) signatures
|
|
63
|
-
let twoArgCandidates = [
|
|
64
|
-
NSSelectorFromString("decodeStringForKey:defaultValue:"),
|
|
65
|
-
NSSelectorFromString("stringForKey:defaultValue:"),
|
|
66
|
-
NSSelectorFromString("getStringForKey:defaultValue:"),
|
|
67
|
-
]
|
|
68
|
-
for sel in twoArgCandidates where mmkv.responds(to: sel) {
|
|
69
|
-
// NSInvocation is cumbersome in Swift; best-effort fallthrough without it
|
|
70
|
-
// Prefer single-arg variants above.
|
|
71
|
-
break
|
|
72
|
-
}
|
|
73
|
-
return nil
|
|
74
|
-
}
|
|
75
39
|
}
|
|
76
40
|
|
|
77
41
|
// Expose a C-ABI symbol the ObjC++ file can call
|
|
@@ -2,6 +2,31 @@ import Foundation
|
|
|
2
2
|
import NitroModules
|
|
3
3
|
|
|
4
4
|
final class NitroFetchClient: HybridNitroFetchClientSpec {
|
|
5
|
+
func requestSync(req: NitroRequest) throws -> NitroResponse {
|
|
6
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
7
|
+
var result: Result<NitroResponse, Error>?
|
|
8
|
+
|
|
9
|
+
Task {
|
|
10
|
+
do {
|
|
11
|
+
let response = try await NitroFetchClient.requestStatic(req)
|
|
12
|
+
result = .success(response)
|
|
13
|
+
} catch {
|
|
14
|
+
result = .failure(error)
|
|
15
|
+
}
|
|
16
|
+
semaphore.signal()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
semaphore.wait()
|
|
20
|
+
|
|
21
|
+
switch result! {
|
|
22
|
+
case .success(let response):
|
|
23
|
+
return response
|
|
24
|
+
case .failure(let error):
|
|
25
|
+
throw error
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Async version - returns Promise<NitroResponse>
|
|
5
30
|
func request(req: NitroRequest) throws -> Promise<NitroResponse> {
|
|
6
31
|
let promise = Promise<NitroResponse>.init()
|
|
7
32
|
Task {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sourceRoot":"../../src","sources":["NitroFetch.nitro.ts"],"mappings":"
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["NitroFetch.nitro.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -3,4 +3,6 @@
|
|
|
3
3
|
import { NitroModules } from 'react-native-nitro-modules';
|
|
4
4
|
// Create singletons once per JS runtime
|
|
5
5
|
export const NitroFetch = NitroModules.createHybridObject('NitroFetch');
|
|
6
|
+
export const NativeStorage = NitroModules.createHybridObject('NativeStorage');
|
|
7
|
+
export const boxedNitroFetch = NitroModules.box(NitroFetch);
|
|
6
8
|
//# sourceMappingURL=NitroInstances.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NitroModules","NitroFetch","createHybridObject"],"sourceRoot":"../../src","sources":["NitroInstances.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;
|
|
1
|
+
{"version":3,"names":["NitroModules","NitroFetch","createHybridObject","NativeStorage","boxedNitroFetch","box"],"sourceRoot":"../../src","sources":["NitroInstances.ts"],"mappings":";;AAAA,SAASA,YAAY,QAAQ,4BAA4B;AAMzD;AACA,OAAO,MAAMC,UAA0B,GACrCD,YAAY,CAACE,kBAAkB,CAAiB,YAAY,CAAC;AAE/D,OAAO,MAAMC,aAAgC,GAC3CH,YAAY,CAACE,kBAAkB,CAAoB,eAAe,CAAC;AAErE,OAAO,MAAME,eAAe,GAAGJ,YAAY,CAACK,GAAG,CAACJ,UAAU,CAAC","ignoreList":[]}
|